ms-functions.php 86 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793
  1. <?php
  2. /**
  3. * Multisite WordPress API
  4. *
  5. * @package WordPress
  6. * @subpackage Multisite
  7. * @since 3.0.0
  8. */
  9. /**
  10. * Gets the network's site and user counts.
  11. *
  12. * @since MU (3.0.0)
  13. *
  14. * @return array Site and user count for the network.
  15. */
  16. function get_sitestats() {
  17. $stats = array(
  18. 'blogs' => get_blog_count(),
  19. 'users' => get_user_count(),
  20. );
  21. return $stats;
  22. }
  23. /**
  24. * Get one of a user's active blogs
  25. *
  26. * Returns the user's primary blog, if they have one and
  27. * it is active. If it's inactive, function returns another
  28. * active blog of the user. If none are found, the user
  29. * is added as a Subscriber to the Dashboard Blog and that blog
  30. * is returned.
  31. *
  32. * @since MU (3.0.0)
  33. *
  34. * @param int $user_id The unique ID of the user
  35. * @return WP_Site|void The blog object
  36. */
  37. function get_active_blog_for_user( $user_id ) {
  38. $blogs = get_blogs_of_user( $user_id );
  39. if ( empty( $blogs ) ) {
  40. return;
  41. }
  42. if ( ! is_multisite() ) {
  43. return $blogs[ get_current_blog_id() ];
  44. }
  45. $primary_blog = get_user_meta( $user_id, 'primary_blog', true );
  46. $first_blog = current( $blogs );
  47. if ( false !== $primary_blog ) {
  48. if ( ! isset( $blogs[ $primary_blog ] ) ) {
  49. update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
  50. $primary = get_site( $first_blog->userblog_id );
  51. } else {
  52. $primary = get_site( $primary_blog );
  53. }
  54. } else {
  55. //TODO Review this call to add_user_to_blog too - to get here the user must have a role on this blog?
  56. $result = add_user_to_blog( $first_blog->userblog_id, $user_id, 'subscriber' );
  57. if ( ! is_wp_error( $result ) ) {
  58. update_user_meta( $user_id, 'primary_blog', $first_blog->userblog_id );
  59. $primary = $first_blog;
  60. }
  61. }
  62. if ( ( ! is_object( $primary ) ) || ( $primary->archived == 1 || $primary->spam == 1 || $primary->deleted == 1 ) ) {
  63. $blogs = get_blogs_of_user( $user_id, true ); // if a user's primary blog is shut down, check their other blogs.
  64. $ret = false;
  65. if ( is_array( $blogs ) && count( $blogs ) > 0 ) {
  66. foreach ( (array) $blogs as $blog_id => $blog ) {
  67. if ( $blog->site_id != get_current_network_id() ) {
  68. continue;
  69. }
  70. $details = get_site( $blog_id );
  71. if ( is_object( $details ) && $details->archived == 0 && $details->spam == 0 && $details->deleted == 0 ) {
  72. $ret = $details;
  73. if ( get_user_meta( $user_id, 'primary_blog', true ) != $blog_id ) {
  74. update_user_meta( $user_id, 'primary_blog', $blog_id );
  75. }
  76. if ( ! get_user_meta( $user_id, 'source_domain', true ) ) {
  77. update_user_meta( $user_id, 'source_domain', $details->domain );
  78. }
  79. break;
  80. }
  81. }
  82. } else {
  83. return;
  84. }
  85. return $ret;
  86. } else {
  87. return $primary;
  88. }
  89. }
  90. /**
  91. * The number of active users in your installation.
  92. *
  93. * The count is cached and updated twice daily. This is not a live count.
  94. *
  95. * @since MU (3.0.0)
  96. * @since 4.8.0 The `$network_id` parameter has been added.
  97. *
  98. * @param int|null $network_id ID of the network. Default is the current network.
  99. * @return int Number of active users on the network.
  100. */
  101. function get_user_count( $network_id = null ) {
  102. return get_network_option( $network_id, 'user_count' );
  103. }
  104. /**
  105. * The number of active sites on your installation.
  106. *
  107. * The count is cached and updated twice daily. This is not a live count.
  108. *
  109. * @since MU (3.0.0)
  110. * @since 3.7.0 The `$network_id` parameter has been deprecated.
  111. * @since 4.8.0 The `$network_id` parameter is now being used.
  112. *
  113. * @param int|null $network_id ID of the network. Default is the current network.
  114. * @return int Number of active sites on the network.
  115. */
  116. function get_blog_count( $network_id = null ) {
  117. return get_network_option( $network_id, 'blog_count' );
  118. }
  119. /**
  120. * Get a blog post from any site on the network.
  121. *
  122. * @since MU (3.0.0)
  123. *
  124. * @param int $blog_id ID of the blog.
  125. * @param int $post_id ID of the post you're looking for.
  126. * @return WP_Post|null WP_Post on success or null on failure
  127. */
  128. function get_blog_post( $blog_id, $post_id ) {
  129. switch_to_blog( $blog_id );
  130. $post = get_post( $post_id );
  131. restore_current_blog();
  132. return $post;
  133. }
  134. /**
  135. * Adds a user to a blog.
  136. *
  137. * Use the {@see 'add_user_to_blog'} action to fire an event when users are added to a blog.
  138. *
  139. * @since MU (3.0.0)
  140. *
  141. * @param int $blog_id ID of the blog you're adding the user to.
  142. * @param int $user_id ID of the user you're adding.
  143. * @param string $role The role you want the user to have
  144. * @return true|WP_Error
  145. */
  146. function add_user_to_blog( $blog_id, $user_id, $role ) {
  147. switch_to_blog( $blog_id );
  148. $user = get_userdata( $user_id );
  149. if ( ! $user ) {
  150. restore_current_blog();
  151. return new WP_Error( 'user_does_not_exist', __( 'The requested user does not exist.' ) );
  152. }
  153. /**
  154. * Filters whether a user should be added to a site.
  155. *
  156. * @since 4.9.0
  157. *
  158. * @param bool|WP_Error $retval True if the user should be added to the site, false
  159. * or error object otherwise.
  160. * @param int $user_id User ID.
  161. * @param string $role User role.
  162. * @param int $blog_id Site ID.
  163. */
  164. $can_add_user = apply_filters( 'can_add_user_to_blog', true, $user_id, $role, $blog_id );
  165. if ( true !== $can_add_user ) {
  166. restore_current_blog();
  167. if ( is_wp_error( $can_add_user ) ) {
  168. return $can_add_user;
  169. }
  170. return new WP_Error( 'user_cannot_be_added', __( 'User cannot be added to this site.' ) );
  171. }
  172. if ( ! get_user_meta( $user_id, 'primary_blog', true ) ) {
  173. update_user_meta( $user_id, 'primary_blog', $blog_id );
  174. $site = get_site( $blog_id );
  175. update_user_meta( $user_id, 'source_domain', $site->domain );
  176. }
  177. $user->set_role( $role );
  178. /**
  179. * Fires immediately after a user is added to a site.
  180. *
  181. * @since MU (3.0.0)
  182. *
  183. * @param int $user_id User ID.
  184. * @param string $role User role.
  185. * @param int $blog_id Blog ID.
  186. */
  187. do_action( 'add_user_to_blog', $user_id, $role, $blog_id );
  188. clean_user_cache( $user_id );
  189. wp_cache_delete( $blog_id . '_user_count', 'blog-details' );
  190. restore_current_blog();
  191. return true;
  192. }
  193. /**
  194. * Remove a user from a blog.
  195. *
  196. * Use the {@see 'remove_user_from_blog'} action to fire an event when
  197. * users are removed from a blog.
  198. *
  199. * Accepts an optional `$reassign` parameter, if you want to
  200. * reassign the user's blog posts to another user upon removal.
  201. *
  202. * @since MU (3.0.0)
  203. *
  204. * @global wpdb $wpdb WordPress database abstraction object.
  205. *
  206. * @param int $user_id ID of the user you're removing.
  207. * @param int $blog_id ID of the blog you're removing the user from.
  208. * @param string $reassign Optional. A user to whom to reassign posts.
  209. * @return true|WP_Error
  210. */
  211. function remove_user_from_blog( $user_id, $blog_id = '', $reassign = '' ) {
  212. global $wpdb;
  213. switch_to_blog( $blog_id );
  214. $user_id = (int) $user_id;
  215. /**
  216. * Fires before a user is removed from a site.
  217. *
  218. * @since MU (3.0.0)
  219. *
  220. * @param int $user_id User ID.
  221. * @param int $blog_id Blog ID.
  222. */
  223. do_action( 'remove_user_from_blog', $user_id, $blog_id );
  224. // If being removed from the primary blog, set a new primary if the user is assigned
  225. // to multiple blogs.
  226. $primary_blog = get_user_meta( $user_id, 'primary_blog', true );
  227. if ( $primary_blog == $blog_id ) {
  228. $new_id = '';
  229. $new_domain = '';
  230. $blogs = get_blogs_of_user( $user_id );
  231. foreach ( (array) $blogs as $blog ) {
  232. if ( $blog->userblog_id == $blog_id ) {
  233. continue;
  234. }
  235. $new_id = $blog->userblog_id;
  236. $new_domain = $blog->domain;
  237. break;
  238. }
  239. update_user_meta( $user_id, 'primary_blog', $new_id );
  240. update_user_meta( $user_id, 'source_domain', $new_domain );
  241. }
  242. // wp_revoke_user($user_id);
  243. $user = get_userdata( $user_id );
  244. if ( ! $user ) {
  245. restore_current_blog();
  246. return new WP_Error( 'user_does_not_exist', __( 'That user does not exist.' ) );
  247. }
  248. $user->remove_all_caps();
  249. $blogs = get_blogs_of_user( $user_id );
  250. if ( count( $blogs ) == 0 ) {
  251. update_user_meta( $user_id, 'primary_blog', '' );
  252. update_user_meta( $user_id, 'source_domain', '' );
  253. }
  254. if ( $reassign != '' ) {
  255. $reassign = (int) $reassign;
  256. $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $user_id ) );
  257. $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $user_id ) );
  258. if ( ! empty( $post_ids ) ) {
  259. $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_author = %d WHERE post_author = %d", $reassign, $user_id ) );
  260. array_walk( $post_ids, 'clean_post_cache' );
  261. }
  262. if ( ! empty( $link_ids ) ) {
  263. $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->links SET link_owner = %d WHERE link_owner = %d", $reassign, $user_id ) );
  264. array_walk( $link_ids, 'clean_bookmark_cache' );
  265. }
  266. }
  267. restore_current_blog();
  268. return true;
  269. }
  270. /**
  271. * Get the permalink for a post on another blog.
  272. *
  273. * @since MU (3.0.0) 1.0
  274. *
  275. * @param int $blog_id ID of the source blog.
  276. * @param int $post_id ID of the desired post.
  277. * @return string The post's permalink
  278. */
  279. function get_blog_permalink( $blog_id, $post_id ) {
  280. switch_to_blog( $blog_id );
  281. $link = get_permalink( $post_id );
  282. restore_current_blog();
  283. return $link;
  284. }
  285. /**
  286. * Get a blog's numeric ID from its URL.
  287. *
  288. * On a subdirectory installation like example.com/blog1/,
  289. * $domain will be the root 'example.com' and $path the
  290. * subdirectory '/blog1/'. With subdomains like blog1.example.com,
  291. * $domain is 'blog1.example.com' and $path is '/'.
  292. *
  293. * @since MU (3.0.0)
  294. *
  295. * @global wpdb $wpdb WordPress database abstraction object.
  296. *
  297. * @param string $domain
  298. * @param string $path Optional. Not required for subdomain installations.
  299. * @return int 0 if no blog found, otherwise the ID of the matching blog
  300. */
  301. function get_blog_id_from_url( $domain, $path = '/' ) {
  302. $domain = strtolower( $domain );
  303. $path = strtolower( $path );
  304. $id = wp_cache_get( md5( $domain . $path ), 'blog-id-cache' );
  305. if ( $id == -1 ) { // blog does not exist
  306. return 0;
  307. } elseif ( $id ) {
  308. return (int) $id;
  309. }
  310. $args = array(
  311. 'domain' => $domain,
  312. 'path' => $path,
  313. 'fields' => 'ids',
  314. 'number' => 1,
  315. 'update_site_meta_cache' => false,
  316. );
  317. $result = get_sites( $args );
  318. $id = array_shift( $result );
  319. if ( ! $id ) {
  320. wp_cache_set( md5( $domain . $path ), -1, 'blog-id-cache' );
  321. return 0;
  322. }
  323. wp_cache_set( md5( $domain . $path ), $id, 'blog-id-cache' );
  324. return $id;
  325. }
  326. // Admin functions
  327. /**
  328. * Checks an email address against a list of banned domains.
  329. *
  330. * This function checks against the Banned Email Domains list
  331. * at wp-admin/network/settings.php. The check is only run on
  332. * self-registrations; user creation at wp-admin/network/users.php
  333. * bypasses this check.
  334. *
  335. * @since MU (3.0.0)
  336. *
  337. * @param string $user_email The email provided by the user at registration.
  338. * @return bool Returns true when the email address is banned.
  339. */
  340. function is_email_address_unsafe( $user_email ) {
  341. $banned_names = get_site_option( 'banned_email_domains' );
  342. if ( $banned_names && ! is_array( $banned_names ) ) {
  343. $banned_names = explode( "\n", $banned_names );
  344. }
  345. $is_email_address_unsafe = false;
  346. if ( $banned_names && is_array( $banned_names ) && false !== strpos( $user_email, '@', 1 ) ) {
  347. $banned_names = array_map( 'strtolower', $banned_names );
  348. $normalized_email = strtolower( $user_email );
  349. list( $email_local_part, $email_domain ) = explode( '@', $normalized_email );
  350. foreach ( $banned_names as $banned_domain ) {
  351. if ( ! $banned_domain ) {
  352. continue;
  353. }
  354. if ( $email_domain == $banned_domain ) {
  355. $is_email_address_unsafe = true;
  356. break;
  357. }
  358. $dotted_domain = ".$banned_domain";
  359. if ( $dotted_domain === substr( $normalized_email, -strlen( $dotted_domain ) ) ) {
  360. $is_email_address_unsafe = true;
  361. break;
  362. }
  363. }
  364. }
  365. /**
  366. * Filters whether an email address is unsafe.
  367. *
  368. * @since 3.5.0
  369. *
  370. * @param bool $is_email_address_unsafe Whether the email address is "unsafe". Default false.
  371. * @param string $user_email User email address.
  372. */
  373. return apply_filters( 'is_email_address_unsafe', $is_email_address_unsafe, $user_email );
  374. }
  375. /**
  376. * Sanitize and validate data required for a user sign-up.
  377. *
  378. * Verifies the validity and uniqueness of user names and user email addresses,
  379. * and checks email addresses against admin-provided domain whitelists and blacklists.
  380. *
  381. * The {@see 'wpmu_validate_user_signup'} hook provides an easy way to modify the sign-up
  382. * process. The value $result, which is passed to the hook, contains both the user-provided
  383. * info and the error messages created by the function. {@see 'wpmu_validate_user_signup'}
  384. * allows you to process the data in any way you'd like, and unset the relevant errors if
  385. * necessary.
  386. *
  387. * @since MU (3.0.0)
  388. *
  389. * @global wpdb $wpdb WordPress database abstraction object.
  390. *
  391. * @param string $user_name The login name provided by the user.
  392. * @param string $user_email The email provided by the user.
  393. * @return array Contains username, email, and error messages.
  394. */
  395. function wpmu_validate_user_signup( $user_name, $user_email ) {
  396. global $wpdb;
  397. $errors = new WP_Error();
  398. $orig_username = $user_name;
  399. $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
  400. if ( $user_name != $orig_username || preg_match( '/[^a-z0-9]/', $user_name ) ) {
  401. $errors->add( 'user_name', __( 'Usernames can only contain lowercase letters (a-z) and numbers.' ) );
  402. $user_name = $orig_username;
  403. }
  404. $user_email = sanitize_email( $user_email );
  405. if ( empty( $user_name ) ) {
  406. $errors->add( 'user_name', __( 'Please enter a username.' ) );
  407. }
  408. $illegal_names = get_site_option( 'illegal_names' );
  409. if ( ! is_array( $illegal_names ) ) {
  410. $illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
  411. add_site_option( 'illegal_names', $illegal_names );
  412. }
  413. if ( in_array( $user_name, $illegal_names ) ) {
  414. $errors->add( 'user_name', __( 'Sorry, that username is not allowed.' ) );
  415. }
  416. /** This filter is documented in wp-includes/user.php */
  417. $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
  418. if ( in_array( strtolower( $user_name ), array_map( 'strtolower', $illegal_logins ) ) ) {
  419. $errors->add( 'user_name', __( 'Sorry, that username is not allowed.' ) );
  420. }
  421. if ( ! is_email( $user_email ) ) {
  422. $errors->add( 'user_email', __( 'Please enter a valid email address.' ) );
  423. } elseif ( is_email_address_unsafe( $user_email ) ) {
  424. $errors->add( 'user_email', __( 'You cannot use that email address to signup. We are having problems with them blocking some of our email. Please use another email provider.' ) );
  425. }
  426. if ( strlen( $user_name ) < 4 ) {
  427. $errors->add( 'user_name', __( 'Username must be at least 4 characters.' ) );
  428. }
  429. if ( strlen( $user_name ) > 60 ) {
  430. $errors->add( 'user_name', __( 'Username may not be longer than 60 characters.' ) );
  431. }
  432. // all numeric?
  433. if ( preg_match( '/^[0-9]*$/', $user_name ) ) {
  434. $errors->add( 'user_name', __( 'Sorry, usernames must have letters too!' ) );
  435. }
  436. $limited_email_domains = get_site_option( 'limited_email_domains' );
  437. if ( is_array( $limited_email_domains ) && ! empty( $limited_email_domains ) ) {
  438. $limited_email_domains = array_map( 'strtolower', $limited_email_domains );
  439. $emaildomain = strtolower( substr( $user_email, 1 + strpos( $user_email, '@' ) ) );
  440. if ( ! in_array( $emaildomain, $limited_email_domains, true ) ) {
  441. $errors->add( 'user_email', __( 'Sorry, that email address is not allowed!' ) );
  442. }
  443. }
  444. // Check if the username has been used already.
  445. if ( username_exists( $user_name ) ) {
  446. $errors->add( 'user_name', __( 'Sorry, that username already exists!' ) );
  447. }
  448. // Check if the email address has been used already.
  449. if ( email_exists( $user_email ) ) {
  450. $errors->add( 'user_email', __( 'Sorry, that email address is already used!' ) );
  451. }
  452. // Has someone already signed up for this username?
  453. $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_login = %s", $user_name ) );
  454. if ( $signup != null ) {
  455. $registered_at = mysql2date( 'U', $signup->registered );
  456. $now = time();
  457. $diff = $now - $registered_at;
  458. // If registered more than two days ago, cancel registration and let this signup go through.
  459. if ( $diff > 2 * DAY_IN_SECONDS ) {
  460. $wpdb->delete( $wpdb->signups, array( 'user_login' => $user_name ) );
  461. } else {
  462. $errors->add( 'user_name', __( 'That username is currently reserved but may be available in a couple of days.' ) );
  463. }
  464. }
  465. $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE user_email = %s", $user_email ) );
  466. if ( $signup != null ) {
  467. $diff = time() - mysql2date( 'U', $signup->registered );
  468. // If registered more than two days ago, cancel registration and let this signup go through.
  469. if ( $diff > 2 * DAY_IN_SECONDS ) {
  470. $wpdb->delete( $wpdb->signups, array( 'user_email' => $user_email ) );
  471. } else {
  472. $errors->add( 'user_email', __( 'That email address has already been used. Please check your inbox for an activation email. It will become available in a couple of days if you do nothing.' ) );
  473. }
  474. }
  475. $result = array(
  476. 'user_name' => $user_name,
  477. 'orig_username' => $orig_username,
  478. 'user_email' => $user_email,
  479. 'errors' => $errors,
  480. );
  481. /**
  482. * Filters the validated user registration details.
  483. *
  484. * This does not allow you to override the username or email of the user during
  485. * registration. The values are solely used for validation and error handling.
  486. *
  487. * @since MU (3.0.0)
  488. *
  489. * @param array $result {
  490. * The array of user name, email and the error messages.
  491. *
  492. * @type string $user_name Sanitized and unique username.
  493. * @type string $orig_username Original username.
  494. * @type string $user_email User email address.
  495. * @type WP_Error $errors WP_Error object containing any errors found.
  496. * }
  497. */
  498. return apply_filters( 'wpmu_validate_user_signup', $result );
  499. }
  500. /**
  501. * Processes new site registrations.
  502. *
  503. * Checks the data provided by the user during blog signup. Verifies
  504. * the validity and uniqueness of blog paths and domains.
  505. *
  506. * This function prevents the current user from registering a new site
  507. * with a blogname equivalent to another user's login name. Passing the
  508. * $user parameter to the function, where $user is the other user, is
  509. * effectively an override of this limitation.
  510. *
  511. * Filter {@see 'wpmu_validate_blog_signup'} if you want to modify
  512. * the way that WordPress validates new site signups.
  513. *
  514. * @since MU (3.0.0)
  515. *
  516. * @global wpdb $wpdb WordPress database abstraction object.
  517. * @global string $domain
  518. *
  519. * @param string $blogname The blog name provided by the user. Must be unique.
  520. * @param string $blog_title The blog title provided by the user.
  521. * @param WP_User|string $user Optional. The user object to check against the new site name.
  522. * @return array Contains the new site data and error messages.
  523. */
  524. function wpmu_validate_blog_signup( $blogname, $blog_title, $user = '' ) {
  525. global $wpdb, $domain;
  526. $current_network = get_network();
  527. $base = $current_network->path;
  528. $blog_title = strip_tags( $blog_title );
  529. $errors = new WP_Error();
  530. $illegal_names = get_site_option( 'illegal_names' );
  531. if ( $illegal_names == false ) {
  532. $illegal_names = array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator' );
  533. add_site_option( 'illegal_names', $illegal_names );
  534. }
  535. /*
  536. * On sub dir installations, some names are so illegal, only a filter can
  537. * spring them from jail.
  538. */
  539. if ( ! is_subdomain_install() ) {
  540. $illegal_names = array_merge( $illegal_names, get_subdirectory_reserved_names() );
  541. }
  542. if ( empty( $blogname ) ) {
  543. $errors->add( 'blogname', __( 'Please enter a site name.' ) );
  544. }
  545. if ( preg_match( '/[^a-z0-9]+/', $blogname ) ) {
  546. $errors->add( 'blogname', __( 'Site names can only contain lowercase letters (a-z) and numbers.' ) );
  547. }
  548. if ( in_array( $blogname, $illegal_names ) ) {
  549. $errors->add( 'blogname', __( 'That name is not allowed.' ) );
  550. }
  551. /**
  552. * Filters the minimum site name length required when validating a site signup.
  553. *
  554. * @since 4.8.0
  555. *
  556. * @param int $length The minimum site name length. Default 4.
  557. */
  558. $minimum_site_name_length = apply_filters( 'minimum_site_name_length', 4 );
  559. if ( strlen( $blogname ) < $minimum_site_name_length ) {
  560. /* translators: %s: Minimum site name length. */
  561. $errors->add( 'blogname', sprintf( _n( 'Site name must be at least %s character.', 'Site name must be at least %s characters.', $minimum_site_name_length ), number_format_i18n( $minimum_site_name_length ) ) );
  562. }
  563. // do not allow users to create a blog that conflicts with a page on the main blog.
  564. if ( ! is_subdomain_install() && $wpdb->get_var( $wpdb->prepare( 'SELECT post_name FROM ' . $wpdb->get_blog_prefix( $current_network->site_id ) . "posts WHERE post_type = 'page' AND post_name = %s", $blogname ) ) ) {
  565. $errors->add( 'blogname', __( 'Sorry, you may not use that site name.' ) );
  566. }
  567. // all numeric?
  568. if ( preg_match( '/^[0-9]*$/', $blogname ) ) {
  569. $errors->add( 'blogname', __( 'Sorry, site names must have letters too!' ) );
  570. }
  571. /**
  572. * Filters the new site name during registration.
  573. *
  574. * The name is the site's subdomain or the site's subdirectory
  575. * path depending on the network settings.
  576. *
  577. * @since MU (3.0.0)
  578. *
  579. * @param string $blogname Site name.
  580. */
  581. $blogname = apply_filters( 'newblogname', $blogname );
  582. $blog_title = wp_unslash( $blog_title );
  583. if ( empty( $blog_title ) ) {
  584. $errors->add( 'blog_title', __( 'Please enter a site title.' ) );
  585. }
  586. // Check if the domain/path has been used already.
  587. if ( is_subdomain_install() ) {
  588. $mydomain = $blogname . '.' . preg_replace( '|^www\.|', '', $domain );
  589. $path = $base;
  590. } else {
  591. $mydomain = "$domain";
  592. $path = $base . $blogname . '/';
  593. }
  594. if ( domain_exists( $mydomain, $path, $current_network->id ) ) {
  595. $errors->add( 'blogname', __( 'Sorry, that site already exists!' ) );
  596. }
  597. if ( username_exists( $blogname ) ) {
  598. if ( ! is_object( $user ) || ( is_object( $user ) && ( $user->user_login != $blogname ) ) ) {
  599. $errors->add( 'blogname', __( 'Sorry, that site is reserved!' ) );
  600. }
  601. }
  602. // Has someone already signed up for this domain?
  603. $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE domain = %s AND path = %s", $mydomain, $path ) ); // TODO: Check email too?
  604. if ( ! empty( $signup ) ) {
  605. $diff = time() - mysql2date( 'U', $signup->registered );
  606. // If registered more than two days ago, cancel registration and let this signup go through.
  607. if ( $diff > 2 * DAY_IN_SECONDS ) {
  608. $wpdb->delete(
  609. $wpdb->signups,
  610. array(
  611. 'domain' => $mydomain,
  612. 'path' => $path,
  613. )
  614. );
  615. } else {
  616. $errors->add( 'blogname', __( 'That site is currently reserved but may be available in a couple days.' ) );
  617. }
  618. }
  619. $result = array(
  620. 'domain' => $mydomain,
  621. 'path' => $path,
  622. 'blogname' => $blogname,
  623. 'blog_title' => $blog_title,
  624. 'user' => $user,
  625. 'errors' => $errors,
  626. );
  627. /**
  628. * Filters site details and error messages following registration.
  629. *
  630. * @since MU (3.0.0)
  631. *
  632. * @param array $result {
  633. * Array of domain, path, blog name, blog title, user and error messages.
  634. *
  635. * @type string $domain Domain for the site.
  636. * @type string $path Path for the site. Used in subdirectory installations.
  637. * @type string $blogname The unique site name (slug).
  638. * @type string $blog_title Blog title.
  639. * @type string|WP_User $user By default, an empty string. A user object if provided.
  640. * @type WP_Error $errors WP_Error containing any errors found.
  641. * }
  642. */
  643. return apply_filters( 'wpmu_validate_blog_signup', $result );
  644. }
  645. /**
  646. * Record site signup information for future activation.
  647. *
  648. * @since MU (3.0.0)
  649. *
  650. * @global wpdb $wpdb WordPress database abstraction object.
  651. *
  652. * @param string $domain The requested domain.
  653. * @param string $path The requested path.
  654. * @param string $title The requested site title.
  655. * @param string $user The user's requested login name.
  656. * @param string $user_email The user's email address.
  657. * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
  658. */
  659. function wpmu_signup_blog( $domain, $path, $title, $user, $user_email, $meta = array() ) {
  660. global $wpdb;
  661. $key = substr( md5( time() . wp_rand() . $domain ), 0, 16 );
  662. /**
  663. * Filters the metadata for a site signup.
  664. *
  665. * The metadata will be serialized prior to storing it in the database.
  666. *
  667. * @since 4.8.0
  668. *
  669. * @param array $meta Signup meta data. Default empty array.
  670. * @param string $domain The requested domain.
  671. * @param string $path The requested path.
  672. * @param string $title The requested site title.
  673. * @param string $user The user's requested login name.
  674. * @param string $user_email The user's email address.
  675. * @param string $key The user's activation key.
  676. */
  677. $meta = apply_filters( 'signup_site_meta', $meta, $domain, $path, $title, $user, $user_email, $key );
  678. $wpdb->insert(
  679. $wpdb->signups,
  680. array(
  681. 'domain' => $domain,
  682. 'path' => $path,
  683. 'title' => $title,
  684. 'user_login' => $user,
  685. 'user_email' => $user_email,
  686. 'registered' => current_time( 'mysql', true ),
  687. 'activation_key' => $key,
  688. 'meta' => serialize( $meta ),
  689. )
  690. );
  691. /**
  692. * Fires after site signup information has been written to the database.
  693. *
  694. * @since 4.4.0
  695. *
  696. * @param string $domain The requested domain.
  697. * @param string $path The requested path.
  698. * @param string $title The requested site title.
  699. * @param string $user The user's requested login name.
  700. * @param string $user_email The user's email address.
  701. * @param string $key The user's activation key.
  702. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  703. */
  704. do_action( 'after_signup_site', $domain, $path, $title, $user, $user_email, $key, $meta );
  705. }
  706. /**
  707. * Record user signup information for future activation.
  708. *
  709. * This function is used when user registration is open but
  710. * new site registration is not.
  711. *
  712. * @since MU (3.0.0)
  713. *
  714. * @global wpdb $wpdb WordPress database abstraction object.
  715. *
  716. * @param string $user The user's requested login name.
  717. * @param string $user_email The user's email address.
  718. * @param array $meta Optional. Signup meta data. Default empty array.
  719. */
  720. function wpmu_signup_user( $user, $user_email, $meta = array() ) {
  721. global $wpdb;
  722. // Format data
  723. $user = preg_replace( '/\s+/', '', sanitize_user( $user, true ) );
  724. $user_email = sanitize_email( $user_email );
  725. $key = substr( md5( time() . wp_rand() . $user_email ), 0, 16 );
  726. /**
  727. * Filters the metadata for a user signup.
  728. *
  729. * The metadata will be serialized prior to storing it in the database.
  730. *
  731. * @since 4.8.0
  732. *
  733. * @param array $meta Signup meta data. Default empty array.
  734. * @param string $user The user's requested login name.
  735. * @param string $user_email The user's email address.
  736. * @param string $key The user's activation key.
  737. */
  738. $meta = apply_filters( 'signup_user_meta', $meta, $user, $user_email, $key );
  739. $wpdb->insert(
  740. $wpdb->signups,
  741. array(
  742. 'domain' => '',
  743. 'path' => '',
  744. 'title' => '',
  745. 'user_login' => $user,
  746. 'user_email' => $user_email,
  747. 'registered' => current_time( 'mysql', true ),
  748. 'activation_key' => $key,
  749. 'meta' => serialize( $meta ),
  750. )
  751. );
  752. /**
  753. * Fires after a user's signup information has been written to the database.
  754. *
  755. * @since 4.4.0
  756. *
  757. * @param string $user The user's requested login name.
  758. * @param string $user_email The user's email address.
  759. * @param string $key The user's activation key.
  760. * @param array $meta Signup meta data. Default empty array.
  761. */
  762. do_action( 'after_signup_user', $user, $user_email, $key, $meta );
  763. }
  764. /**
  765. * Send a confirmation request email to a user when they sign up for a new site. The new site will not become active
  766. * until the confirmation link is clicked.
  767. *
  768. * This is the notification function used when site registration
  769. * is enabled.
  770. *
  771. * Filter {@see 'wpmu_signup_blog_notification'} to bypass this function or
  772. * replace it with your own notification behavior.
  773. *
  774. * Filter {@see 'wpmu_signup_blog_notification_email'} and
  775. * {@see 'wpmu_signup_blog_notification_subject'} to change the content
  776. * and subject line of the email sent to newly registered users.
  777. *
  778. * @since MU (3.0.0)
  779. *
  780. * @param string $domain The new blog domain.
  781. * @param string $path The new blog path.
  782. * @param string $title The site title.
  783. * @param string $user_login The user's login name.
  784. * @param string $user_email The user's email address.
  785. * @param string $key The activation key created in wpmu_signup_blog()
  786. * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
  787. * @return bool
  788. */
  789. function wpmu_signup_blog_notification( $domain, $path, $title, $user_login, $user_email, $key, $meta = array() ) {
  790. /**
  791. * Filters whether to bypass the new site email notification.
  792. *
  793. * @since MU (3.0.0)
  794. *
  795. * @param string|bool $domain Site domain.
  796. * @param string $path Site path.
  797. * @param string $title Site title.
  798. * @param string $user_login User login name.
  799. * @param string $user_email User email address.
  800. * @param string $key Activation key created in wpmu_signup_blog().
  801. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  802. */
  803. if ( ! apply_filters( 'wpmu_signup_blog_notification', $domain, $path, $title, $user_login, $user_email, $key, $meta ) ) {
  804. return false;
  805. }
  806. // Send email with activation link.
  807. if ( ! is_subdomain_install() || get_current_network_id() != 1 ) {
  808. $activate_url = network_site_url( "wp-activate.php?key=$key" );
  809. } else {
  810. $activate_url = "http://{$domain}{$path}wp-activate.php?key=$key"; // @todo use *_url() API
  811. }
  812. $activate_url = esc_url( $activate_url );
  813. $admin_email = get_site_option( 'admin_email' );
  814. if ( $admin_email == '' ) {
  815. $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
  816. }
  817. $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
  818. $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
  819. $user = get_user_by( 'login', $user_login );
  820. $switched_locale = switch_to_locale( get_user_locale( $user ) );
  821. $message = sprintf(
  822. /**
  823. * Filters the message content of the new blog notification email.
  824. *
  825. * Content should be formatted for transmission via wp_mail().
  826. *
  827. * @since MU (3.0.0)
  828. *
  829. * @param string $content Content of the notification email.
  830. * @param string $domain Site domain.
  831. * @param string $path Site path.
  832. * @param string $title Site title.
  833. * @param string $user_login User login name.
  834. * @param string $user_email User email address.
  835. * @param string $key Activation key created in wpmu_signup_blog().
  836. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  837. */
  838. apply_filters(
  839. 'wpmu_signup_blog_notification_email',
  840. /* translators: New site notification email. 1: Activation URL, 2: New site URL. */
  841. __( "To activate your blog, please click the following link:\n\n%1\$s\n\nAfter you activate, you will receive *another email* with your login.\n\nAfter you activate, you can visit your site here:\n\n%2\$s" ),
  842. $domain,
  843. $path,
  844. $title,
  845. $user_login,
  846. $user_email,
  847. $key,
  848. $meta
  849. ),
  850. $activate_url,
  851. esc_url( "http://{$domain}{$path}" ),
  852. $key
  853. );
  854. $subject = sprintf(
  855. /**
  856. * Filters the subject of the new blog notification email.
  857. *
  858. * @since MU (3.0.0)
  859. *
  860. * @param string $subject Subject of the notification email.
  861. * @param string $domain Site domain.
  862. * @param string $path Site path.
  863. * @param string $title Site title.
  864. * @param string $user_login User login name.
  865. * @param string $user_email User email address.
  866. * @param string $key Activation key created in wpmu_signup_blog().
  867. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  868. */
  869. apply_filters(
  870. 'wpmu_signup_blog_notification_subject',
  871. /* translators: New site notification email subject. 1: Network title, 2: New site URL. */
  872. _x( '[%1$s] Activate %2$s', 'New site notification email subject' ),
  873. $domain,
  874. $path,
  875. $title,
  876. $user_login,
  877. $user_email,
  878. $key,
  879. $meta
  880. ),
  881. $from_name,
  882. esc_url( 'http://' . $domain . $path )
  883. );
  884. wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
  885. if ( $switched_locale ) {
  886. restore_previous_locale();
  887. }
  888. return true;
  889. }
  890. /**
  891. * Send a confirmation request email to a user when they sign up for a new user account (without signing up for a site
  892. * at the same time). The user account will not become active until the confirmation link is clicked.
  893. *
  894. * This is the notification function used when no new site has
  895. * been requested.
  896. *
  897. * Filter {@see 'wpmu_signup_user_notification'} to bypass this function or
  898. * replace it with your own notification behavior.
  899. *
  900. * Filter {@see 'wpmu_signup_user_notification_email'} and
  901. * {@see 'wpmu_signup_user_notification_subject'} to change the content
  902. * and subject line of the email sent to newly registered users.
  903. *
  904. * @since MU (3.0.0)
  905. *
  906. * @param string $user_login The user's login name.
  907. * @param string $user_email The user's email address.
  908. * @param string $key The activation key created in wpmu_signup_user()
  909. * @param array $meta Optional. Signup meta data. Default empty array.
  910. * @return bool
  911. */
  912. function wpmu_signup_user_notification( $user_login, $user_email, $key, $meta = array() ) {
  913. /**
  914. * Filters whether to bypass the email notification for new user sign-up.
  915. *
  916. * @since MU (3.0.0)
  917. *
  918. * @param string $user_login User login name.
  919. * @param string $user_email User email address.
  920. * @param string $key Activation key created in wpmu_signup_user().
  921. * @param array $meta Signup meta data. Default empty array.
  922. */
  923. if ( ! apply_filters( 'wpmu_signup_user_notification', $user_login, $user_email, $key, $meta ) ) {
  924. return false;
  925. }
  926. $user = get_user_by( 'login', $user_login );
  927. $switched_locale = switch_to_locale( get_user_locale( $user ) );
  928. // Send email with activation link.
  929. $admin_email = get_site_option( 'admin_email' );
  930. if ( $admin_email == '' ) {
  931. $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
  932. }
  933. $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
  934. $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
  935. $message = sprintf(
  936. /**
  937. * Filters the content of the notification email for new user sign-up.
  938. *
  939. * Content should be formatted for transmission via wp_mail().
  940. *
  941. * @since MU (3.0.0)
  942. *
  943. * @param string $content Content of the notification email.
  944. * @param string $user_login User login name.
  945. * @param string $user_email User email address.
  946. * @param string $key Activation key created in wpmu_signup_user().
  947. * @param array $meta Signup meta data. Default empty array.
  948. */
  949. apply_filters(
  950. 'wpmu_signup_user_notification_email',
  951. /* translators: New user notification email. %s: Activation URL. */
  952. __( "To activate your user, please click the following link:\n\n%s\n\nAfter you activate, you will receive *another email* with your login." ),
  953. $user_login,
  954. $user_email,
  955. $key,
  956. $meta
  957. ),
  958. site_url( "wp-activate.php?key=$key" )
  959. );
  960. $subject = sprintf(
  961. /**
  962. * Filters the subject of the notification email of new user signup.
  963. *
  964. * @since MU (3.0.0)
  965. *
  966. * @param string $subject Subject of the notification email.
  967. * @param string $user_login User login name.
  968. * @param string $user_email User email address.
  969. * @param string $key Activation key created in wpmu_signup_user().
  970. * @param array $meta Signup meta data. Default empty array.
  971. */
  972. apply_filters(
  973. 'wpmu_signup_user_notification_subject',
  974. /* translators: New user notification email subject. 1: Network title, 2: New user login. */
  975. _x( '[%1$s] Activate %2$s', 'New user notification email subject' ),
  976. $user_login,
  977. $user_email,
  978. $key,
  979. $meta
  980. ),
  981. $from_name,
  982. $user_login
  983. );
  984. wp_mail( $user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
  985. if ( $switched_locale ) {
  986. restore_previous_locale();
  987. }
  988. return true;
  989. }
  990. /**
  991. * Activate a signup.
  992. *
  993. * Hook to {@see 'wpmu_activate_user'} or {@see 'wpmu_activate_blog'} for events
  994. * that should happen only when users or sites are self-created (since
  995. * those actions are not called when users and sites are created
  996. * by a Super Admin).
  997. *
  998. * @since MU (3.0.0)
  999. *
  1000. * @global wpdb $wpdb WordPress database abstraction object.
  1001. *
  1002. * @param string $key The activation key provided to the user.
  1003. * @return array|WP_Error An array containing information about the activated user and/or blog
  1004. */
  1005. function wpmu_activate_signup( $key ) {
  1006. global $wpdb;
  1007. $signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->signups WHERE activation_key = %s", $key ) );
  1008. if ( empty( $signup ) ) {
  1009. return new WP_Error( 'invalid_key', __( 'Invalid activation key.' ) );
  1010. }
  1011. if ( $signup->active ) {
  1012. if ( empty( $signup->domain ) ) {
  1013. return new WP_Error( 'already_active', __( 'The user is already active.' ), $signup );
  1014. } else {
  1015. return new WP_Error( 'already_active', __( 'The site is already active.' ), $signup );
  1016. }
  1017. }
  1018. $meta = maybe_unserialize( $signup->meta );
  1019. $password = wp_generate_password( 12, false );
  1020. $user_id = username_exists( $signup->user_login );
  1021. if ( ! $user_id ) {
  1022. $user_id = wpmu_create_user( $signup->user_login, $password, $signup->user_email );
  1023. } else {
  1024. $user_already_exists = true;
  1025. }
  1026. if ( ! $user_id ) {
  1027. return new WP_Error( 'create_user', __( 'Could not create user' ), $signup );
  1028. }
  1029. $now = current_time( 'mysql', true );
  1030. if ( empty( $signup->domain ) ) {
  1031. $wpdb->update(
  1032. $wpdb->signups,
  1033. array(
  1034. 'active' => 1,
  1035. 'activated' => $now,
  1036. ),
  1037. array( 'activation_key' => $key )
  1038. );
  1039. if ( isset( $user_already_exists ) ) {
  1040. return new WP_Error( 'user_already_exists', __( 'That username is already activated.' ), $signup );
  1041. }
  1042. /**
  1043. * Fires immediately after a new user is activated.
  1044. *
  1045. * @since MU (3.0.0)
  1046. *
  1047. * @param int $user_id User ID.
  1048. * @param int $password User password.
  1049. * @param array $meta Signup meta data.
  1050. */
  1051. do_action( 'wpmu_activate_user', $user_id, $password, $meta );
  1052. return array(
  1053. 'user_id' => $user_id,
  1054. 'password' => $password,
  1055. 'meta' => $meta,
  1056. );
  1057. }
  1058. $blog_id = wpmu_create_blog( $signup->domain, $signup->path, $signup->title, $user_id, $meta, get_current_network_id() );
  1059. // TODO: What to do if we create a user but cannot create a blog?
  1060. if ( is_wp_error( $blog_id ) ) {
  1061. // If blog is taken, that means a previous attempt to activate this blog failed in between creating the blog and
  1062. // setting the activation flag. Let's just set the active flag and instruct the user to reset their password.
  1063. if ( 'blog_taken' == $blog_id->get_error_code() ) {
  1064. $blog_id->add_data( $signup );
  1065. $wpdb->update(
  1066. $wpdb->signups,
  1067. array(
  1068. 'active' => 1,
  1069. 'activated' => $now,
  1070. ),
  1071. array( 'activation_key' => $key )
  1072. );
  1073. }
  1074. return $blog_id;
  1075. }
  1076. $wpdb->update(
  1077. $wpdb->signups,
  1078. array(
  1079. 'active' => 1,
  1080. 'activated' => $now,
  1081. ),
  1082. array( 'activation_key' => $key )
  1083. );
  1084. /**
  1085. * Fires immediately after a site is activated.
  1086. *
  1087. * @since MU (3.0.0)
  1088. *
  1089. * @param int $blog_id Blog ID.
  1090. * @param int $user_id User ID.
  1091. * @param int $password User password.
  1092. * @param string $signup_title Site title.
  1093. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  1094. */
  1095. do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $signup->title, $meta );
  1096. return array(
  1097. 'blog_id' => $blog_id,
  1098. 'user_id' => $user_id,
  1099. 'password' => $password,
  1100. 'title' => $signup->title,
  1101. 'meta' => $meta,
  1102. );
  1103. }
  1104. /**
  1105. * Create a user.
  1106. *
  1107. * This function runs when a user self-registers as well as when
  1108. * a Super Admin creates a new user. Hook to {@see 'wpmu_new_user'} for events
  1109. * that should affect all new users, but only on Multisite (otherwise
  1110. * use {@see'user_register'}).
  1111. *
  1112. * @since MU (3.0.0)
  1113. *
  1114. * @param string $user_name The new user's login name.
  1115. * @param string $password The new user's password.
  1116. * @param string $email The new user's email address.
  1117. * @return int|false Returns false on failure, or int $user_id on success
  1118. */
  1119. function wpmu_create_user( $user_name, $password, $email ) {
  1120. $user_name = preg_replace( '/\s+/', '', sanitize_user( $user_name, true ) );
  1121. $user_id = wp_create_user( $user_name, $password, $email );
  1122. if ( is_wp_error( $user_id ) ) {
  1123. return false;
  1124. }
  1125. // Newly created users have no roles or caps until they are added to a blog.
  1126. delete_user_option( $user_id, 'capabilities' );
  1127. delete_user_option( $user_id, 'user_level' );
  1128. /**
  1129. * Fires immediately after a new user is created.
  1130. *
  1131. * @since MU (3.0.0)
  1132. *
  1133. * @param int $user_id User ID.
  1134. */
  1135. do_action( 'wpmu_new_user', $user_id );
  1136. return $user_id;
  1137. }
  1138. /**
  1139. * Create a site.
  1140. *
  1141. * This function runs when a user self-registers a new site as well
  1142. * as when a Super Admin creates a new site. Hook to {@see 'wpmu_new_blog'}
  1143. * for events that should affect all new sites.
  1144. *
  1145. * On subdirectory installations, $domain is the same as the main site's
  1146. * domain, and the path is the subdirectory name (eg 'example.com'
  1147. * and '/blog1/'). On subdomain installations, $domain is the new subdomain +
  1148. * root domain (eg 'blog1.example.com'), and $path is '/'.
  1149. *
  1150. * @since MU (3.0.0)
  1151. *
  1152. * @param string $domain The new site's domain.
  1153. * @param string $path The new site's path.
  1154. * @param string $title The new site's title.
  1155. * @param int $user_id The user ID of the new site's admin.
  1156. * @param array $options Optional. Array of key=>value pairs used to set initial site options.
  1157. * If valid status keys are included ('public', 'archived', 'mature',
  1158. * 'spam', 'deleted', or 'lang_id') the given site status(es) will be
  1159. * updated. Otherwise, keys and values will be used to set options for
  1160. * the new site. Default empty array.
  1161. * @param int $network_id Optional. Network ID. Only relevant on multi-network installations.
  1162. * @return int|WP_Error Returns WP_Error object on failure, the new site ID on success.
  1163. */
  1164. function wpmu_create_blog( $domain, $path, $title, $user_id, $options = array(), $network_id = 1 ) {
  1165. $defaults = array(
  1166. 'public' => 0,
  1167. );
  1168. $options = wp_parse_args( $options, $defaults );
  1169. $title = strip_tags( $title );
  1170. $user_id = (int) $user_id;
  1171. // Check if the domain has been used already. We should return an error message.
  1172. if ( domain_exists( $domain, $path, $network_id ) ) {
  1173. return new WP_Error( 'blog_taken', __( 'Sorry, that site already exists!' ) );
  1174. }
  1175. if ( ! wp_installing() ) {
  1176. wp_installing( true );
  1177. }
  1178. $site_data_whitelist = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
  1179. $site_data = array_merge(
  1180. array(
  1181. 'domain' => $domain,
  1182. 'path' => $path,
  1183. 'network_id' => $network_id,
  1184. ),
  1185. array_intersect_key( $options, array_flip( $site_data_whitelist ) )
  1186. );
  1187. // Data to pass to wp_initialize_site().
  1188. $site_initialization_data = array(
  1189. 'title' => $title,
  1190. 'user_id' => $user_id,
  1191. 'options' => array_diff_key( $options, array_flip( $site_data_whitelist ) ),
  1192. );
  1193. $blog_id = wp_insert_site( array_merge( $site_data, $site_initialization_data ) );
  1194. if ( is_wp_error( $blog_id ) ) {
  1195. return $blog_id;
  1196. }
  1197. wp_cache_set( 'last_changed', microtime(), 'sites' );
  1198. return $blog_id;
  1199. }
  1200. /**
  1201. * Notifies the network admin that a new site has been activated.
  1202. *
  1203. * Filter {@see 'newblog_notify_siteadmin'} to change the content of
  1204. * the notification email.
  1205. *
  1206. * @since MU (3.0.0)
  1207. * @since 5.1.0 $blog_id now supports input from the {@see 'wp_initialize_site'} action.
  1208. *
  1209. * @param WP_Site|int $blog_id The new site's object or ID.
  1210. * @param string $deprecated Not used.
  1211. * @return bool
  1212. */
  1213. function newblog_notify_siteadmin( $blog_id, $deprecated = '' ) {
  1214. if ( is_object( $blog_id ) ) {
  1215. $blog_id = $blog_id->blog_id;
  1216. }
  1217. if ( get_site_option( 'registrationnotification' ) != 'yes' ) {
  1218. return false;
  1219. }
  1220. $email = get_site_option( 'admin_email' );
  1221. if ( is_email( $email ) == false ) {
  1222. return false;
  1223. }
  1224. $options_site_url = esc_url( network_admin_url( 'settings.php' ) );
  1225. switch_to_blog( $blog_id );
  1226. $blogname = get_option( 'blogname' );
  1227. $siteurl = site_url();
  1228. restore_current_blog();
  1229. $msg = sprintf(
  1230. /* translators: New site notification email. 1: Site URL, 2: User IP address, 3: URL to Network Settings screen. */
  1231. __(
  1232. 'New Site: %1$s
  1233. URL: %2$s
  1234. Remote IP address: %3$s
  1235. Disable these notifications: %4$s'
  1236. ),
  1237. $blogname,
  1238. $siteurl,
  1239. wp_unslash( $_SERVER['REMOTE_ADDR'] ),
  1240. $options_site_url
  1241. );
  1242. /**
  1243. * Filters the message body of the new site activation email sent
  1244. * to the network administrator.
  1245. *
  1246. * @since MU (3.0.0)
  1247. *
  1248. * @param string $msg Email body.
  1249. */
  1250. $msg = apply_filters( 'newblog_notify_siteadmin', $msg );
  1251. /* translators: New site notification email subject. %s: New site URL. */
  1252. wp_mail( $email, sprintf( __( 'New Site Registration: %s' ), $siteurl ), $msg );
  1253. return true;
  1254. }
  1255. /**
  1256. * Notifies the network admin that a new user has been activated.
  1257. *
  1258. * Filter {@see 'newuser_notify_siteadmin'} to change the content of
  1259. * the notification email.
  1260. *
  1261. * @since MU (3.0.0)
  1262. *
  1263. * @param int $user_id The new user's ID.
  1264. * @return bool
  1265. */
  1266. function newuser_notify_siteadmin( $user_id ) {
  1267. if ( get_site_option( 'registrationnotification' ) != 'yes' ) {
  1268. return false;
  1269. }
  1270. $email = get_site_option( 'admin_email' );
  1271. if ( is_email( $email ) == false ) {
  1272. return false;
  1273. }
  1274. $user = get_userdata( $user_id );
  1275. $options_site_url = esc_url( network_admin_url( 'settings.php' ) );
  1276. $msg = sprintf(
  1277. /* translators: New user notification email. 1: User login, 2: User IP address, 3: URL to Network Settings screen. */
  1278. __(
  1279. 'New User: %1$s
  1280. Remote IP address: %2$s
  1281. Disable these notifications: %3$s'
  1282. ),
  1283. $user->user_login,
  1284. wp_unslash( $_SERVER['REMOTE_ADDR'] ),
  1285. $options_site_url
  1286. );
  1287. /**
  1288. * Filters the message body of the new user activation email sent
  1289. * to the network administrator.
  1290. *
  1291. * @since MU (3.0.0)
  1292. *
  1293. * @param string $msg Email body.
  1294. * @param WP_User $user WP_User instance of the new user.
  1295. */
  1296. $msg = apply_filters( 'newuser_notify_siteadmin', $msg, $user );
  1297. /* translators: New user notification email subject. %s: User login. */
  1298. wp_mail( $email, sprintf( __( 'New User Registration: %s' ), $user->user_login ), $msg );
  1299. return true;
  1300. }
  1301. /**
  1302. * Checks whether a site name is already taken.
  1303. *
  1304. * The name is the site's subdomain or the site's subdirectory
  1305. * path depending on the network settings.
  1306. *
  1307. * Used during the new site registration process to ensure
  1308. * that each site name is unique.
  1309. *
  1310. * @since MU (3.0.0)
  1311. *
  1312. * @param string $domain The domain to be checked.
  1313. * @param string $path The path to be checked.
  1314. * @param int $network_id Optional. Network ID. Relevant only on multi-network installations.
  1315. * @return int|null The site ID if the site name exists, null otherwise.
  1316. */
  1317. function domain_exists( $domain, $path, $network_id = 1 ) {
  1318. $path = trailingslashit( $path );
  1319. $args = array(
  1320. 'network_id' => $network_id,
  1321. 'domain' => $domain,
  1322. 'path' => $path,
  1323. 'fields' => 'ids',
  1324. 'number' => 1,
  1325. 'update_site_meta_cache' => false,
  1326. );
  1327. $result = get_sites( $args );
  1328. $result = array_shift( $result );
  1329. /**
  1330. * Filters whether a site name is taken.
  1331. *
  1332. * The name is the site's subdomain or the site's subdirectory
  1333. * path depending on the network settings.
  1334. *
  1335. * @since 3.5.0
  1336. *
  1337. * @param int|null $result The site ID if the site name exists, null otherwise.
  1338. * @param string $domain Domain to be checked.
  1339. * @param string $path Path to be checked.
  1340. * @param int $network_id Network ID. Relevant only on multi-network installations.
  1341. */
  1342. return apply_filters( 'domain_exists', $result, $domain, $path, $network_id );
  1343. }
  1344. /**
  1345. * Notify a user that their blog activation has been successful.
  1346. *
  1347. * Filter {@see 'wpmu_welcome_notification'} to disable or bypass.
  1348. *
  1349. * Filter {@see 'update_welcome_email'} and {@see 'update_welcome_subject'} to
  1350. * modify the content and subject line of the notification email.
  1351. *
  1352. * @since MU (3.0.0)
  1353. *
  1354. * @param int $blog_id Blog ID.
  1355. * @param int $user_id User ID.
  1356. * @param string $password User password.
  1357. * @param string $title Site title.
  1358. * @param array $meta Optional. Signup meta data. By default, contains the requested privacy setting and lang_id.
  1359. * @return bool
  1360. */
  1361. function wpmu_welcome_notification( $blog_id, $user_id, $password, $title, $meta = array() ) {
  1362. $current_network = get_network();
  1363. /**
  1364. * Filters whether to bypass the welcome email after site activation.
  1365. *
  1366. * Returning false disables the welcome email.
  1367. *
  1368. * @since MU (3.0.0)
  1369. *
  1370. * @param int|bool $blog_id Blog ID.
  1371. * @param int $user_id User ID.
  1372. * @param string $password User password.
  1373. * @param string $title Site title.
  1374. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  1375. */
  1376. if ( ! apply_filters( 'wpmu_welcome_notification', $blog_id, $user_id, $password, $title, $meta ) ) {
  1377. return false;
  1378. }
  1379. $user = get_userdata( $user_id );
  1380. $switched_locale = switch_to_locale( get_user_locale( $user ) );
  1381. $welcome_email = get_site_option( 'welcome_email' );
  1382. if ( $welcome_email == false ) {
  1383. /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */
  1384. $welcome_email = __(
  1385. 'Howdy USERNAME,
  1386. Your new SITE_NAME site has been successfully set up at:
  1387. BLOG_URL
  1388. You can log in to the administrator account with the following information:
  1389. Username: USERNAME
  1390. Password: PASSWORD
  1391. Log in here: BLOG_URLwp-login.php
  1392. We hope you enjoy your new site. Thanks!
  1393. --The Team @ SITE_NAME'
  1394. );
  1395. }
  1396. $url = get_blogaddress_by_id( $blog_id );
  1397. $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
  1398. $welcome_email = str_replace( 'BLOG_TITLE', $title, $welcome_email );
  1399. $welcome_email = str_replace( 'BLOG_URL', $url, $welcome_email );
  1400. $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
  1401. $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
  1402. /**
  1403. * Filters the content of the welcome email after site activation.
  1404. *
  1405. * Content should be formatted for transmission via wp_mail().
  1406. *
  1407. * @since MU (3.0.0)
  1408. *
  1409. * @param string $welcome_email Message body of the email.
  1410. * @param int $blog_id Blog ID.
  1411. * @param int $user_id User ID.
  1412. * @param string $password User password.
  1413. * @param string $title Site title.
  1414. * @param array $meta Signup meta data. By default, contains the requested privacy setting and lang_id.
  1415. */
  1416. $welcome_email = apply_filters( 'update_welcome_email', $welcome_email, $blog_id, $user_id, $password, $title, $meta );
  1417. $admin_email = get_site_option( 'admin_email' );
  1418. if ( $admin_email == '' ) {
  1419. $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
  1420. }
  1421. $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
  1422. $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
  1423. $message = $welcome_email;
  1424. if ( empty( $current_network->site_name ) ) {
  1425. $current_network->site_name = 'WordPress';
  1426. }
  1427. /* translators: New site notification email subject. 1: Network title, 2: New site title. */
  1428. $subject = __( 'New %1$s Site: %2$s' );
  1429. /**
  1430. * Filters the subject of the welcome email after site activation.
  1431. *
  1432. * @since MU (3.0.0)
  1433. *
  1434. * @param string $subject Subject of the email.
  1435. */
  1436. $subject = apply_filters( 'update_welcome_subject', sprintf( $subject, $current_network->site_name, wp_unslash( $title ) ) );
  1437. wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
  1438. if ( $switched_locale ) {
  1439. restore_previous_locale();
  1440. }
  1441. return true;
  1442. }
  1443. /**
  1444. * Notify a user that their account activation has been successful.
  1445. *
  1446. * Filter {@see 'wpmu_welcome_user_notification'} to disable or bypass.
  1447. *
  1448. * Filter {@see 'update_welcome_user_email'} and {@see 'update_welcome_user_subject'} to
  1449. * modify the content and subject line of the notification email.
  1450. *
  1451. * @since MU (3.0.0)
  1452. *
  1453. * @param int $user_id User ID.
  1454. * @param string $password User password.
  1455. * @param array $meta Optional. Signup meta data. Default empty array.
  1456. * @return bool
  1457. */
  1458. function wpmu_welcome_user_notification( $user_id, $password, $meta = array() ) {
  1459. $current_network = get_network();
  1460. /**
  1461. * Filters whether to bypass the welcome email after user activation.
  1462. *
  1463. * Returning false disables the welcome email.
  1464. *
  1465. * @since MU (3.0.0)
  1466. *
  1467. * @param int $user_id User ID.
  1468. * @param string $password User password.
  1469. * @param array $meta Signup meta data. Default empty array.
  1470. */
  1471. if ( ! apply_filters( 'wpmu_welcome_user_notification', $user_id, $password, $meta ) ) {
  1472. return false;
  1473. }
  1474. $welcome_email = get_site_option( 'welcome_user_email' );
  1475. $user = get_userdata( $user_id );
  1476. $switched_locale = switch_to_locale( get_user_locale( $user ) );
  1477. /**
  1478. * Filters the content of the welcome email after user activation.
  1479. *
  1480. * Content should be formatted for transmission via wp_mail().
  1481. *
  1482. * @since MU (3.0.0)
  1483. *
  1484. * @param string $welcome_email The message body of the account activation success email.
  1485. * @param int $user_id User ID.
  1486. * @param string $password User password.
  1487. * @param array $meta Signup meta data. Default empty array.
  1488. */
  1489. $welcome_email = apply_filters( 'update_welcome_user_email', $welcome_email, $user_id, $password, $meta );
  1490. $welcome_email = str_replace( 'SITE_NAME', $current_network->site_name, $welcome_email );
  1491. $welcome_email = str_replace( 'USERNAME', $user->user_login, $welcome_email );
  1492. $welcome_email = str_replace( 'PASSWORD', $password, $welcome_email );
  1493. $welcome_email = str_replace( 'LOGINLINK', wp_login_url(), $welcome_email );
  1494. $admin_email = get_site_option( 'admin_email' );
  1495. if ( $admin_email == '' ) {
  1496. $admin_email = 'support@' . $_SERVER['SERVER_NAME'];
  1497. }
  1498. $from_name = get_site_option( 'site_name' ) == '' ? 'WordPress' : esc_html( get_site_option( 'site_name' ) );
  1499. $message_headers = "From: \"{$from_name}\" <{$admin_email}>\n" . 'Content-Type: text/plain; charset="' . get_option( 'blog_charset' ) . "\"\n";
  1500. $message = $welcome_email;
  1501. if ( empty( $current_network->site_name ) ) {
  1502. $current_network->site_name = 'WordPress';
  1503. }
  1504. /* translators: New user notification email subject. 1: Network title, 2: New user login. */
  1505. $subject = __( 'New %1$s User: %2$s' );
  1506. /**
  1507. * Filters the subject of the welcome email after user activation.
  1508. *
  1509. * @since MU (3.0.0)
  1510. *
  1511. * @param string $subject Subject of the email.
  1512. */
  1513. $subject = apply_filters( 'update_welcome_user_subject', sprintf( $subject, $current_network->site_name, $user->user_login ) );
  1514. wp_mail( $user->user_email, wp_specialchars_decode( $subject ), $message, $message_headers );
  1515. if ( $switched_locale ) {
  1516. restore_previous_locale();
  1517. }
  1518. return true;
  1519. }
  1520. /**
  1521. * Get the current network.
  1522. *
  1523. * Returns an object containing the 'id', 'domain', 'path', and 'site_name'
  1524. * properties of the network being viewed.
  1525. *
  1526. * @see wpmu_current_site()
  1527. *
  1528. * @since MU (3.0.0)
  1529. *
  1530. * @global WP_Network $current_site
  1531. *
  1532. * @return WP_Network
  1533. */
  1534. function get_current_site() {
  1535. global $current_site;
  1536. return $current_site;
  1537. }
  1538. /**
  1539. * Get a user's most recent post.
  1540. *
  1541. * Walks through each of a user's blogs to find the post with
  1542. * the most recent post_date_gmt.
  1543. *
  1544. * @since MU (3.0.0)
  1545. *
  1546. * @global wpdb $wpdb WordPress database abstraction object.
  1547. *
  1548. * @param int $user_id
  1549. * @return array Contains the blog_id, post_id, post_date_gmt, and post_gmt_ts
  1550. */
  1551. function get_most_recent_post_of_user( $user_id ) {
  1552. global $wpdb;
  1553. $user_blogs = get_blogs_of_user( (int) $user_id );
  1554. $most_recent_post = array();
  1555. // Walk through each blog and get the most recent post
  1556. // published by $user_id
  1557. foreach ( (array) $user_blogs as $blog ) {
  1558. $prefix = $wpdb->get_blog_prefix( $blog->userblog_id );
  1559. $recent_post = $wpdb->get_row( $wpdb->prepare( "SELECT ID, post_date_gmt FROM {$prefix}posts WHERE post_author = %d AND post_type = 'post' AND post_status = 'publish' ORDER BY post_date_gmt DESC LIMIT 1", $user_id ), ARRAY_A );
  1560. // Make sure we found a post
  1561. if ( isset( $recent_post['ID'] ) ) {
  1562. $post_gmt_ts = strtotime( $recent_post['post_date_gmt'] );
  1563. // If this is the first post checked or if this post is
  1564. // newer than the current recent post, make it the new
  1565. // most recent post.
  1566. if ( ! isset( $most_recent_post['post_gmt_ts'] ) || ( $post_gmt_ts > $most_recent_post['post_gmt_ts'] ) ) {
  1567. $most_recent_post = array(
  1568. 'blog_id' => $blog->userblog_id,
  1569. 'post_id' => $recent_post['ID'],
  1570. 'post_date_gmt' => $recent_post['post_date_gmt'],
  1571. 'post_gmt_ts' => $post_gmt_ts,
  1572. );
  1573. }
  1574. }
  1575. }
  1576. return $most_recent_post;
  1577. }
  1578. // Misc functions
  1579. /**
  1580. * Check an array of MIME types against a whitelist.
  1581. *
  1582. * WordPress ships with a set of allowed upload filetypes,
  1583. * which is defined in wp-includes/functions.php in
  1584. * get_allowed_mime_types(). This function is used to filter
  1585. * that list against the filetype whitelist provided by Multisite
  1586. * Super Admins at wp-admin/network/settings.php.
  1587. *
  1588. * @since MU (3.0.0)
  1589. *
  1590. * @param array $mimes
  1591. * @return array
  1592. */
  1593. function check_upload_mimes( $mimes ) {
  1594. $site_exts = explode( ' ', get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) );
  1595. $site_mimes = array();
  1596. foreach ( $site_exts as $ext ) {
  1597. foreach ( $mimes as $ext_pattern => $mime ) {
  1598. if ( $ext != '' && strpos( $ext_pattern, $ext ) !== false ) {
  1599. $site_mimes[ $ext_pattern ] = $mime;
  1600. }
  1601. }
  1602. }
  1603. return $site_mimes;
  1604. }
  1605. /**
  1606. * Update a blog's post count.
  1607. *
  1608. * WordPress MS stores a blog's post count as an option so as
  1609. * to avoid extraneous COUNTs when a blog's details are fetched
  1610. * with get_site(). This function is called when posts are published
  1611. * or unpublished to make sure the count stays current.
  1612. *
  1613. * @since MU (3.0.0)
  1614. *
  1615. * @global wpdb $wpdb WordPress database abstraction object.
  1616. *
  1617. * @param string $deprecated Not used.
  1618. */
  1619. function update_posts_count( $deprecated = '' ) {
  1620. global $wpdb;
  1621. update_option( 'post_count', (int) $wpdb->get_var( "SELECT COUNT(ID) FROM {$wpdb->posts} WHERE post_status = 'publish' and post_type = 'post'" ) );
  1622. }
  1623. /**
  1624. * Logs the user email, IP, and registration date of a new site.
  1625. *
  1626. * @since MU (3.0.0)
  1627. * @since 5.1.0 Parameters now support input from the {@see 'wp_initialize_site'} action.
  1628. *
  1629. * @global wpdb $wpdb WordPress database abstraction object.
  1630. *
  1631. * @param WP_Site|int $blog_id The new site's object or ID.
  1632. * @param int|array $user_id User ID, or array of arguments including 'user_id'.
  1633. */
  1634. function wpmu_log_new_registrations( $blog_id, $user_id ) {
  1635. global $wpdb;
  1636. if ( is_object( $blog_id ) ) {
  1637. $blog_id = $blog_id->blog_id;
  1638. }
  1639. if ( is_array( $user_id ) ) {
  1640. $user_id = ! empty( $user_id['user_id'] ) ? $user_id['user_id'] : 0;
  1641. }
  1642. $user = get_userdata( (int) $user_id );
  1643. if ( $user ) {
  1644. $wpdb->insert(
  1645. $wpdb->registration_log,
  1646. array(
  1647. 'email' => $user->user_email,
  1648. 'IP' => preg_replace( '/[^0-9., ]/', '', wp_unslash( $_SERVER['REMOTE_ADDR'] ) ),
  1649. 'blog_id' => $blog_id,
  1650. 'date_registered' => current_time( 'mysql' ),
  1651. )
  1652. );
  1653. }
  1654. }
  1655. /**
  1656. * Maintains a canonical list of terms by syncing terms created for each blog with the global terms table.
  1657. *
  1658. * @since 3.0.0
  1659. *
  1660. * @see term_id_filter
  1661. *
  1662. * @global wpdb $wpdb WordPress database abstraction object.
  1663. * @staticvar int $global_terms_recurse
  1664. *
  1665. * @param int $term_id An ID for a term on the current blog.
  1666. * @param string $deprecated Not used.
  1667. * @return int An ID from the global terms table mapped from $term_id.
  1668. */
  1669. function global_terms( $term_id, $deprecated = '' ) {
  1670. global $wpdb;
  1671. static $global_terms_recurse = null;
  1672. if ( ! global_terms_enabled() ) {
  1673. return $term_id;
  1674. }
  1675. // prevent a race condition
  1676. $recurse_start = false;
  1677. if ( $global_terms_recurse === null ) {
  1678. $recurse_start = true;
  1679. $global_terms_recurse = 1;
  1680. } elseif ( 10 < $global_terms_recurse++ ) {
  1681. return $term_id;
  1682. }
  1683. $term_id = intval( $term_id );
  1684. $c = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->terms WHERE term_id = %d", $term_id ) );
  1685. $global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE category_nicename = %s", $c->slug ) );
  1686. if ( $global_id == null ) {
  1687. $used_global_id = $wpdb->get_var( $wpdb->prepare( "SELECT cat_ID FROM $wpdb->sitecategories WHERE cat_ID = %d", $c->term_id ) );
  1688. if ( null == $used_global_id ) {
  1689. $wpdb->insert(
  1690. $wpdb->sitecategories,
  1691. array(
  1692. 'cat_ID' => $term_id,
  1693. 'cat_name' => $c->name,
  1694. 'category_nicename' => $c->slug,
  1695. )
  1696. );
  1697. $global_id = $wpdb->insert_id;
  1698. if ( empty( $global_id ) ) {
  1699. return $term_id;
  1700. }
  1701. } else {
  1702. $max_global_id = $wpdb->get_var( "SELECT MAX(cat_ID) FROM $wpdb->sitecategories" );
  1703. $max_local_id = $wpdb->get_var( "SELECT MAX(term_id) FROM $wpdb->terms" );
  1704. $new_global_id = max( $max_global_id, $max_local_id ) + mt_rand( 100, 400 );
  1705. $wpdb->insert(
  1706. $wpdb->sitecategories,
  1707. array(
  1708. 'cat_ID' => $new_global_id,
  1709. 'cat_name' => $c->name,
  1710. 'category_nicename' => $c->slug,
  1711. )
  1712. );
  1713. $global_id = $wpdb->insert_id;
  1714. }
  1715. } elseif ( $global_id != $term_id ) {
  1716. $local_id = $wpdb->get_var( $wpdb->prepare( "SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $global_id ) );
  1717. if ( null != $local_id ) {
  1718. global_terms( $local_id );
  1719. if ( 10 < $global_terms_recurse ) {
  1720. $global_id = $term_id;
  1721. }
  1722. }
  1723. }
  1724. if ( $global_id != $term_id ) {
  1725. if ( get_option( 'default_category' ) == $term_id ) {
  1726. update_option( 'default_category', $global_id );
  1727. }
  1728. $wpdb->update( $wpdb->terms, array( 'term_id' => $global_id ), array( 'term_id' => $term_id ) );
  1729. $wpdb->update( $wpdb->term_taxonomy, array( 'term_id' => $global_id ), array( 'term_id' => $term_id ) );
  1730. $wpdb->update( $wpdb->term_taxonomy, array( 'parent' => $global_id ), array( 'parent' => $term_id ) );
  1731. clean_term_cache( $term_id );
  1732. }
  1733. if ( $recurse_start ) {
  1734. $global_terms_recurse = null;
  1735. }
  1736. return $global_id;
  1737. }
  1738. /**
  1739. * Ensure that the current site's domain is listed in the allowed redirect host list.
  1740. *
  1741. * @see wp_validate_redirect()
  1742. * @since MU (3.0.0)
  1743. *
  1744. * @param array|string $deprecated Not used.
  1745. * @return array The current site's domain
  1746. */
  1747. function redirect_this_site( $deprecated = '' ) {
  1748. return array( get_network()->domain );
  1749. }
  1750. /**
  1751. * Check whether an upload is too big.
  1752. *
  1753. * @since MU (3.0.0)
  1754. *
  1755. * @blessed
  1756. *
  1757. * @param array $upload
  1758. * @return string|array If the upload is under the size limit, $upload is returned. Otherwise returns an error message.
  1759. */
  1760. function upload_is_file_too_big( $upload ) {
  1761. if ( ! is_array( $upload ) || defined( 'WP_IMPORTING' ) || get_site_option( 'upload_space_check_disabled' ) ) {
  1762. return $upload;
  1763. }
  1764. if ( strlen( $upload['bits'] ) > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
  1765. /* translators: %s: Maximum allowed file size in kilobytes. */
  1766. return sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ) . '<br />', get_site_option( 'fileupload_maxk', 1500 ) );
  1767. }
  1768. return $upload;
  1769. }
  1770. /**
  1771. * Add a nonce field to the signup page.
  1772. *
  1773. * @since MU (3.0.0)
  1774. */
  1775. function signup_nonce_fields() {
  1776. $id = mt_rand();
  1777. echo "<input type='hidden' name='signup_form_id' value='{$id}' />";
  1778. wp_nonce_field( 'signup_form_' . $id, '_signup_form', false );
  1779. }
  1780. /**
  1781. * Process the signup nonce created in signup_nonce_fields().
  1782. *
  1783. * @since MU (3.0.0)
  1784. *
  1785. * @param array $result
  1786. * @return array
  1787. */
  1788. function signup_nonce_check( $result ) {
  1789. if ( ! strpos( $_SERVER['PHP_SELF'], 'wp-signup.php' ) ) {
  1790. return $result;
  1791. }
  1792. if ( ! wp_verify_nonce( $_POST['_signup_form'], 'signup_form_' . $_POST['signup_form_id'] ) ) {
  1793. $result['errors']->add( 'invalid_nonce', __( 'Unable to submit this form, please try again.' ) );
  1794. }
  1795. return $result;
  1796. }
  1797. /**
  1798. * Correct 404 redirects when NOBLOGREDIRECT is defined.
  1799. *
  1800. * @since MU (3.0.0)
  1801. */
  1802. function maybe_redirect_404() {
  1803. if ( is_main_site() && is_404() && defined( 'NOBLOGREDIRECT' ) ) {
  1804. /**
  1805. * Filters the redirect URL for 404s on the main site.
  1806. *
  1807. * The filter is only evaluated if the NOBLOGREDIRECT constant is defined.
  1808. *
  1809. * @since 3.0.0
  1810. *
  1811. * @param string $no_blog_redirect The redirect URL defined in NOBLOGREDIRECT.
  1812. */
  1813. $destination = apply_filters( 'blog_redirect_404', NOBLOGREDIRECT );
  1814. if ( $destination ) {
  1815. if ( $destination == '%siteurl%' ) {
  1816. $destination = network_home_url();
  1817. }
  1818. wp_redirect( $destination );
  1819. exit();
  1820. }
  1821. }
  1822. }
  1823. /**
  1824. * Add a new user to a blog by visiting /newbloguser/{key}/.
  1825. *
  1826. * This will only work when the user's details are saved as an option
  1827. * keyed as 'new_user_{key}', where '{key}' is a hash generated for the user to be
  1828. * added, as when a user is invited through the regular WP Add User interface.
  1829. *
  1830. * @since MU (3.0.0)
  1831. */
  1832. function maybe_add_existing_user_to_blog() {
  1833. if ( false === strpos( $_SERVER['REQUEST_URI'], '/newbloguser/' ) ) {
  1834. return;
  1835. }
  1836. $parts = explode( '/', $_SERVER['REQUEST_URI'] );
  1837. $key = array_pop( $parts );
  1838. if ( $key == '' ) {
  1839. $key = array_pop( $parts );
  1840. }
  1841. $details = get_option( 'new_user_' . $key );
  1842. if ( ! empty( $details ) ) {
  1843. delete_option( 'new_user_' . $key );
  1844. }
  1845. if ( empty( $details ) || is_wp_error( add_existing_user_to_blog( $details ) ) ) {
  1846. wp_die(
  1847. sprintf(
  1848. /* translators: %s: Home URL. */
  1849. __( 'An error occurred adding you to this site. Back to the <a href="%s">homepage</a>.' ),
  1850. home_url()
  1851. )
  1852. );
  1853. }
  1854. wp_die(
  1855. sprintf(
  1856. /* translators: 1: Home URL, 2: Admin URL. */
  1857. __( 'You have been added to this site. Please visit the <a href="%1$s">homepage</a> or <a href="%2$s">log in</a> using your username and password.' ),
  1858. home_url(),
  1859. admin_url()
  1860. ),
  1861. __( 'WordPress &rsaquo; Success' ),
  1862. array( 'response' => 200 )
  1863. );
  1864. }
  1865. /**
  1866. * Add a user to a blog based on details from maybe_add_existing_user_to_blog().
  1867. *
  1868. * @since MU (3.0.0)
  1869. *
  1870. * @param array $details
  1871. * @return true|WP_Error|void
  1872. */
  1873. function add_existing_user_to_blog( $details = false ) {
  1874. if ( is_array( $details ) ) {
  1875. $blog_id = get_current_blog_id();
  1876. $result = add_user_to_blog( $blog_id, $details['user_id'], $details['role'] );
  1877. /**
  1878. * Fires immediately after an existing user is added to a site.
  1879. *
  1880. * @since MU (3.0.0)
  1881. *
  1882. * @param int $user_id User ID.
  1883. * @param mixed $result True on success or a WP_Error object if the user doesn't exist
  1884. * or could not be added.
  1885. */
  1886. do_action( 'added_existing_user', $details['user_id'], $result );
  1887. return $result;
  1888. }
  1889. }
  1890. /**
  1891. * Adds a newly created user to the appropriate blog
  1892. *
  1893. * To add a user in general, use add_user_to_blog(). This function
  1894. * is specifically hooked into the {@see 'wpmu_activate_user'} action.
  1895. *
  1896. * @since MU (3.0.0)
  1897. * @see add_user_to_blog()
  1898. *
  1899. * @param int $user_id
  1900. * @param mixed $password Ignored.
  1901. * @param array $meta
  1902. */
  1903. function add_new_user_to_blog( $user_id, $password, $meta ) {
  1904. if ( ! empty( $meta['add_to_blog'] ) ) {
  1905. $blog_id = $meta['add_to_blog'];
  1906. $role = $meta['new_role'];
  1907. remove_user_from_blog( $user_id, get_network()->site_id ); // remove user from main blog.
  1908. $result = add_user_to_blog( $blog_id, $user_id, $role );
  1909. if ( ! is_wp_error( $result ) ) {
  1910. update_user_meta( $user_id, 'primary_blog', $blog_id );
  1911. }
  1912. }
  1913. }
  1914. /**
  1915. * Correct From host on outgoing mail to match the site domain
  1916. *
  1917. * @since MU (3.0.0)
  1918. *
  1919. * @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
  1920. */
  1921. function fix_phpmailer_messageid( $phpmailer ) {
  1922. $phpmailer->Hostname = get_network()->domain;
  1923. }
  1924. /**
  1925. * Check to see whether a user is marked as a spammer, based on user login.
  1926. *
  1927. * @since MU (3.0.0)
  1928. *
  1929. * @param string|WP_User $user Optional. Defaults to current user. WP_User object,
  1930. * or user login name as a string.
  1931. * @return bool
  1932. */
  1933. function is_user_spammy( $user = null ) {
  1934. if ( ! ( $user instanceof WP_User ) ) {
  1935. if ( $user ) {
  1936. $user = get_user_by( 'login', $user );
  1937. } else {
  1938. $user = wp_get_current_user();
  1939. }
  1940. }
  1941. return $user && isset( $user->spam ) && 1 == $user->spam;
  1942. }
  1943. /**
  1944. * Update this blog's 'public' setting in the global blogs table.
  1945. *
  1946. * Public blogs have a setting of 1, private blogs are 0.
  1947. *
  1948. * @since MU (3.0.0)
  1949. *
  1950. * @param int $old_value
  1951. * @param int $value The new public value
  1952. */
  1953. function update_blog_public( $old_value, $value ) {
  1954. update_blog_status( get_current_blog_id(), 'public', (int) $value );
  1955. }
  1956. /**
  1957. * Check whether users can self-register, based on Network settings.
  1958. *
  1959. * @since MU (3.0.0)
  1960. *
  1961. * @return bool
  1962. */
  1963. function users_can_register_signup_filter() {
  1964. $registration = get_site_option( 'registration' );
  1965. return ( $registration == 'all' || $registration == 'user' );
  1966. }
  1967. /**
  1968. * Ensure that the welcome message is not empty. Currently unused.
  1969. *
  1970. * @since MU (3.0.0)
  1971. *
  1972. * @param string $text
  1973. * @return string
  1974. */
  1975. function welcome_user_msg_filter( $text ) {
  1976. if ( ! $text ) {
  1977. remove_filter( 'site_option_welcome_user_email', 'welcome_user_msg_filter' );
  1978. /* translators: Do not translate USERNAME, PASSWORD, LOGINLINK, SITE_NAME: those are placeholders. */
  1979. $text = __(
  1980. 'Howdy USERNAME,
  1981. Your new account is set up.
  1982. You can log in with the following information:
  1983. Username: USERNAME
  1984. Password: PASSWORD
  1985. LOGINLINK
  1986. Thanks!
  1987. --The Team @ SITE_NAME'
  1988. );
  1989. update_site_option( 'welcome_user_email', $text );
  1990. }
  1991. return $text;
  1992. }
  1993. /**
  1994. * Whether to force SSL on content.
  1995. *
  1996. * @since 2.8.5
  1997. *
  1998. * @staticvar bool $forced_content
  1999. *
  2000. * @param bool $force
  2001. * @return bool True if forced, false if not forced.
  2002. */
  2003. function force_ssl_content( $force = '' ) {
  2004. static $forced_content = false;
  2005. if ( '' != $force ) {
  2006. $old_forced = $forced_content;
  2007. $forced_content = $force;
  2008. return $old_forced;
  2009. }
  2010. return $forced_content;
  2011. }
  2012. /**
  2013. * Formats a URL to use https.
  2014. *
  2015. * Useful as a filter.
  2016. *
  2017. * @since 2.8.5
  2018. *
  2019. * @param string $url URL
  2020. * @return string URL with https as the scheme
  2021. */
  2022. function filter_SSL( $url ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
  2023. if ( ! is_string( $url ) ) {
  2024. return get_bloginfo( 'url' ); // Return home blog url with proper scheme
  2025. }
  2026. if ( force_ssl_content() && is_ssl() ) {
  2027. $url = set_url_scheme( $url, 'https' );
  2028. }
  2029. return $url;
  2030. }
  2031. /**
  2032. * Schedule update of the network-wide counts for the current network.
  2033. *
  2034. * @since 3.1.0
  2035. */
  2036. function wp_schedule_update_network_counts() {
  2037. if ( ! is_main_site() ) {
  2038. return;
  2039. }
  2040. if ( ! wp_next_scheduled( 'update_network_counts' ) && ! wp_installing() ) {
  2041. wp_schedule_event( time(), 'twicedaily', 'update_network_counts' );
  2042. }
  2043. }
  2044. /**
  2045. * Update the network-wide counts for the current network.
  2046. *
  2047. * @since 3.1.0
  2048. * @since 4.8.0 The `$network_id` parameter has been added.
  2049. *
  2050. * @param int|null $network_id ID of the network. Default is the current network.
  2051. */
  2052. function wp_update_network_counts( $network_id = null ) {
  2053. wp_update_network_user_counts( $network_id );
  2054. wp_update_network_site_counts( $network_id );
  2055. }
  2056. /**
  2057. * Update the count of sites for the current network.
  2058. *
  2059. * If enabled through the {@see 'enable_live_network_counts'} filter, update the sites count
  2060. * on a network when a site is created or its status is updated.
  2061. *
  2062. * @since 3.7.0
  2063. * @since 4.8.0 The `$network_id` parameter has been added.
  2064. *
  2065. * @param int|null $network_id ID of the network. Default is the current network.
  2066. */
  2067. function wp_maybe_update_network_site_counts( $network_id = null ) {
  2068. $is_small_network = ! wp_is_large_network( 'sites', $network_id );
  2069. /**
  2070. * Filters whether to update network site or user counts when a new site is created.
  2071. *
  2072. * @since 3.7.0
  2073. *
  2074. * @see wp_is_large_network()
  2075. *
  2076. * @param bool $small_network Whether the network is considered small.
  2077. * @param string $context Context. Either 'users' or 'sites'.
  2078. */
  2079. if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'sites' ) ) {
  2080. return;
  2081. }
  2082. wp_update_network_site_counts( $network_id );
  2083. }
  2084. /**
  2085. * Update the network-wide users count.
  2086. *
  2087. * If enabled through the {@see 'enable_live_network_counts'} filter, update the users count
  2088. * on a network when a user is created or its status is updated.
  2089. *
  2090. * @since 3.7.0
  2091. * @since 4.8.0 The `$network_id` parameter has been added.
  2092. *
  2093. * @param int|null $network_id ID of the network. Default is the current network.
  2094. */
  2095. function wp_maybe_update_network_user_counts( $network_id = null ) {
  2096. $is_small_network = ! wp_is_large_network( 'users', $network_id );
  2097. /** This filter is documented in wp-includes/ms-functions.php */
  2098. if ( ! apply_filters( 'enable_live_network_counts', $is_small_network, 'users' ) ) {
  2099. return;
  2100. }
  2101. wp_update_network_user_counts( $network_id );
  2102. }
  2103. /**
  2104. * Update the network-wide site count.
  2105. *
  2106. * @since 3.7.0
  2107. * @since 4.8.0 The `$network_id` parameter has been added.
  2108. *
  2109. * @param int|null $network_id ID of the network. Default is the current network.
  2110. */
  2111. function wp_update_network_site_counts( $network_id = null ) {
  2112. $network_id = (int) $network_id;
  2113. if ( ! $network_id ) {
  2114. $network_id = get_current_network_id();
  2115. }
  2116. $count = get_sites(
  2117. array(
  2118. 'network_id' => $network_id,
  2119. 'spam' => 0,
  2120. 'deleted' => 0,
  2121. 'archived' => 0,
  2122. 'count' => true,
  2123. 'update_site_meta_cache' => false,
  2124. )
  2125. );
  2126. update_network_option( $network_id, 'blog_count', $count );
  2127. }
  2128. /**
  2129. * Update the network-wide user count.
  2130. *
  2131. * @since 3.7.0
  2132. * @since 4.8.0 The `$network_id` parameter has been added.
  2133. *
  2134. * @global wpdb $wpdb WordPress database abstraction object.
  2135. *
  2136. * @param int|null $network_id ID of the network. Default is the current network.
  2137. */
  2138. function wp_update_network_user_counts( $network_id = null ) {
  2139. global $wpdb;
  2140. $count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
  2141. update_network_option( $network_id, 'user_count', $count );
  2142. }
  2143. /**
  2144. * Returns the space used by the current site.
  2145. *
  2146. * @since 3.5.0
  2147. *
  2148. * @return int Used space in megabytes.
  2149. */
  2150. function get_space_used() {
  2151. /**
  2152. * Filters the amount of storage space used by the current site, in megabytes.
  2153. *
  2154. * @since 3.5.0
  2155. *
  2156. * @param int|false $space_used The amount of used space, in megabytes. Default false.
  2157. */
  2158. $space_used = apply_filters( 'pre_get_space_used', false );
  2159. if ( false === $space_used ) {
  2160. $upload_dir = wp_upload_dir();
  2161. $space_used = get_dirsize( $upload_dir['basedir'] ) / MB_IN_BYTES;
  2162. }
  2163. return $space_used;
  2164. }
  2165. /**
  2166. * Returns the upload quota for the current blog.
  2167. *
  2168. * @since MU (3.0.0)
  2169. *
  2170. * @return int Quota in megabytes
  2171. */
  2172. function get_space_allowed() {
  2173. $space_allowed = get_option( 'blog_upload_space' );
  2174. if ( ! is_numeric( $space_allowed ) ) {
  2175. $space_allowed = get_site_option( 'blog_upload_space' );
  2176. }
  2177. if ( ! is_numeric( $space_allowed ) ) {
  2178. $space_allowed = 100;
  2179. }
  2180. /**
  2181. * Filters the upload quota for the current site.
  2182. *
  2183. * @since 3.7.0
  2184. *
  2185. * @param int $space_allowed Upload quota in megabytes for the current blog.
  2186. */
  2187. return apply_filters( 'get_space_allowed', $space_allowed );
  2188. }
  2189. /**
  2190. * Determines if there is any upload space left in the current blog's quota.
  2191. *
  2192. * @since 3.0.0
  2193. *
  2194. * @return int of upload space available in bytes
  2195. */
  2196. function get_upload_space_available() {
  2197. $allowed = get_space_allowed();
  2198. if ( $allowed < 0 ) {
  2199. $allowed = 0;
  2200. }
  2201. $space_allowed = $allowed * MB_IN_BYTES;
  2202. if ( get_site_option( 'upload_space_check_disabled' ) ) {
  2203. return $space_allowed;
  2204. }
  2205. $space_used = get_space_used() * MB_IN_BYTES;
  2206. if ( ( $space_allowed - $space_used ) <= 0 ) {
  2207. return 0;
  2208. }
  2209. return $space_allowed - $space_used;
  2210. }
  2211. /**
  2212. * Determines if there is any upload space left in the current blog's quota.
  2213. *
  2214. * @since 3.0.0
  2215. * @return bool True if space is available, false otherwise.
  2216. */
  2217. function is_upload_space_available() {
  2218. if ( get_site_option( 'upload_space_check_disabled' ) ) {
  2219. return true;
  2220. }
  2221. return (bool) get_upload_space_available();
  2222. }
  2223. /**
  2224. * Filters the maximum upload file size allowed, in bytes.
  2225. *
  2226. * @since 3.0.0
  2227. *
  2228. * @param int $size Upload size limit in bytes.
  2229. * @return int Upload size limit in bytes.
  2230. */
  2231. function upload_size_limit_filter( $size ) {
  2232. $fileupload_maxk = KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 );
  2233. if ( get_site_option( 'upload_space_check_disabled' ) ) {
  2234. return min( $size, $fileupload_maxk );
  2235. }
  2236. return min( $size, $fileupload_maxk, get_upload_space_available() );
  2237. }
  2238. /**
  2239. * Whether or not we have a large network.
  2240. *
  2241. * The default criteria for a large network is either more than 10,000 users or more than 10,000 sites.
  2242. * Plugins can alter this criteria using the {@see 'wp_is_large_network'} filter.
  2243. *
  2244. * @since 3.3.0
  2245. * @since 4.8.0 The `$network_id` parameter has been added.
  2246. *
  2247. * @param string $using 'sites or 'users'. Default is 'sites'.
  2248. * @param int|null $network_id ID of the network. Default is the current network.
  2249. * @return bool True if the network meets the criteria for large. False otherwise.
  2250. */
  2251. function wp_is_large_network( $using = 'sites', $network_id = null ) {
  2252. $network_id = (int) $network_id;
  2253. if ( ! $network_id ) {
  2254. $network_id = get_current_network_id();
  2255. }
  2256. if ( 'users' == $using ) {
  2257. $count = get_user_count( $network_id );
  2258. /**
  2259. * Filters whether the network is considered large.
  2260. *
  2261. * @since 3.3.0
  2262. * @since 4.8.0 The `$network_id` parameter has been added.
  2263. *
  2264. * @param bool $is_large_network Whether the network has more than 10000 users or sites.
  2265. * @param string $component The component to count. Accepts 'users', or 'sites'.
  2266. * @param int $count The count of items for the component.
  2267. * @param int $network_id The ID of the network being checked.
  2268. */
  2269. return apply_filters( 'wp_is_large_network', $count > 10000, 'users', $count, $network_id );
  2270. }
  2271. $count = get_blog_count( $network_id );
  2272. /** This filter is documented in wp-includes/ms-functions.php */
  2273. return apply_filters( 'wp_is_large_network', $count > 10000, 'sites', $count, $network_id );
  2274. }
  2275. /**
  2276. * Retrieves a list of reserved site on a sub-directory Multisite installation.
  2277. *
  2278. * @since 4.4.0
  2279. *
  2280. * @return array $names Array of reserved subdirectory names.
  2281. */
  2282. function get_subdirectory_reserved_names() {
  2283. $names = array(
  2284. 'page',
  2285. 'comments',
  2286. 'blog',
  2287. 'files',
  2288. 'feed',
  2289. 'wp-admin',
  2290. 'wp-content',
  2291. 'wp-includes',
  2292. 'wp-json',
  2293. 'embed',
  2294. );
  2295. /**
  2296. * Filters reserved site names on a sub-directory Multisite installation.
  2297. *
  2298. * @since 3.0.0
  2299. * @since 4.4.0 'wp-admin', 'wp-content', 'wp-includes', 'wp-json', and 'embed' were added
  2300. * to the reserved names list.
  2301. *
  2302. * @param array $subdirectory_reserved_names Array of reserved names.
  2303. */
  2304. return apply_filters( 'subdirectory_reserved_names', $names );
  2305. }
  2306. /**
  2307. * Send a confirmation request email when a change of network admin email address is attempted.
  2308. *
  2309. * The new network admin address will not become active until confirmed.
  2310. *
  2311. * @since 4.9.0
  2312. *
  2313. * @param string $old_value The old network admin email address.
  2314. * @param string $value The proposed new network admin email address.
  2315. */
  2316. function update_network_option_new_admin_email( $old_value, $value ) {
  2317. if ( $value == get_site_option( 'admin_email' ) || ! is_email( $value ) ) {
  2318. return;
  2319. }
  2320. $hash = md5( $value . time() . mt_rand() );
  2321. $new_admin_email = array(
  2322. 'hash' => $hash,
  2323. 'newemail' => $value,
  2324. );
  2325. update_site_option( 'network_admin_hash', $new_admin_email );
  2326. $switched_locale = switch_to_locale( get_user_locale() );
  2327. /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
  2328. $email_text = __(
  2329. 'Howdy ###USERNAME###,
  2330. You recently requested to have the network admin email address on
  2331. your network changed.
  2332. If this is correct, please click on the following link to change it:
  2333. ###ADMIN_URL###
  2334. You can safely ignore and delete this email if you do not want to
  2335. take this action.
  2336. This email has been sent to ###EMAIL###
  2337. Regards,
  2338. All at ###SITENAME###
  2339. ###SITEURL###'
  2340. );
  2341. /**
  2342. * Filters the text of the email sent when a change of network admin email address is attempted.
  2343. *
  2344. * The following strings have a special meaning and will get replaced dynamically:
  2345. * ###USERNAME### The current user's username.
  2346. * ###ADMIN_URL### The link to click on to confirm the email change.
  2347. * ###EMAIL### The proposed new network admin email address.
  2348. * ###SITENAME### The name of the network.
  2349. * ###SITEURL### The URL to the network.
  2350. *
  2351. * @since 4.9.0
  2352. *
  2353. * @param string $email_text Text in the email.
  2354. * @param array $new_admin_email {
  2355. * Data relating to the new network admin email address.
  2356. *
  2357. * @type string $hash The secure hash used in the confirmation link URL.
  2358. * @type string $newemail The proposed new network admin email address.
  2359. * }
  2360. */
  2361. $content = apply_filters( 'new_network_admin_email_content', $email_text, $new_admin_email );
  2362. $current_user = wp_get_current_user();
  2363. $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
  2364. $content = str_replace( '###ADMIN_URL###', esc_url( network_admin_url( 'settings.php?network_admin_hash=' . $hash ) ), $content );
  2365. $content = str_replace( '###EMAIL###', $value, $content );
  2366. $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES ), $content );
  2367. $content = str_replace( '###SITEURL###', network_home_url(), $content );
  2368. wp_mail(
  2369. $value,
  2370. sprintf(
  2371. /* translators: Email change notification email subject. %s: Network title. */
  2372. __( '[%s] Network Admin Email Change Request' ),
  2373. wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES )
  2374. ),
  2375. $content
  2376. );
  2377. if ( $switched_locale ) {
  2378. restore_previous_locale();
  2379. }
  2380. }
  2381. /**
  2382. * Send an email to the old network admin email address when the network admin email address changes.
  2383. *
  2384. * @since 4.9.0
  2385. *
  2386. * @param string $option_name The relevant database option name.
  2387. * @param string $new_email The new network admin email address.
  2388. * @param string $old_email The old network admin email address.
  2389. * @param int $network_id ID of the network.
  2390. */
  2391. function wp_network_admin_email_change_notification( $option_name, $new_email, $old_email, $network_id ) {
  2392. $send = true;
  2393. // Don't send the notification to the default 'admin_email' value.
  2394. if ( 'you@example.com' === $old_email ) {
  2395. $send = false;
  2396. }
  2397. /**
  2398. * Filters whether to send the network admin email change notification email.
  2399. *
  2400. * @since 4.9.0
  2401. *
  2402. * @param bool $send Whether to send the email notification.
  2403. * @param string $old_email The old network admin email address.
  2404. * @param string $new_email The new network admin email address.
  2405. * @param int $network_id ID of the network.
  2406. */
  2407. $send = apply_filters( 'send_network_admin_email_change_email', $send, $old_email, $new_email, $network_id );
  2408. if ( ! $send ) {
  2409. return;
  2410. }
  2411. /* translators: Do not translate OLD_EMAIL, NEW_EMAIL, SITENAME, SITEURL: those are placeholders. */
  2412. $email_change_text = __(
  2413. 'Hi,
  2414. This notice confirms that the network admin email address was changed on ###SITENAME###.
  2415. The new network admin email address is ###NEW_EMAIL###.
  2416. This email has been sent to ###OLD_EMAIL###
  2417. Regards,
  2418. All at ###SITENAME###
  2419. ###SITEURL###'
  2420. );
  2421. $email_change_email = array(
  2422. 'to' => $old_email,
  2423. /* translators: Network admin email change notification email subject. %s: Network title. */
  2424. 'subject' => __( '[%s] Network Admin Email Changed' ),
  2425. 'message' => $email_change_text,
  2426. 'headers' => '',
  2427. );
  2428. // get network name
  2429. $network_name = wp_specialchars_decode( get_site_option( 'site_name' ), ENT_QUOTES );
  2430. /**
  2431. * Filters the contents of the email notification sent when the network admin email address is changed.
  2432. *
  2433. * @since 4.9.0
  2434. *
  2435. * @param array $email_change_email {
  2436. * Used to build wp_mail().
  2437. *
  2438. * @type string $to The intended recipient.
  2439. * @type string $subject The subject of the email.
  2440. * @type string $message The content of the email.
  2441. * The following strings have a special meaning and will get replaced dynamically:
  2442. * - ###OLD_EMAIL### The old network admin email address.
  2443. * - ###NEW_EMAIL### The new network admin email address.
  2444. * - ###SITENAME### The name of the network.
  2445. * - ###SITEURL### The URL to the site.
  2446. * @type string $headers Headers.
  2447. * }
  2448. * @param string $old_email The old network admin email address.
  2449. * @param string $new_email The new network admin email address.
  2450. * @param int $network_id ID of the network.
  2451. */
  2452. $email_change_email = apply_filters( 'network_admin_email_change_email', $email_change_email, $old_email, $new_email, $network_id );
  2453. $email_change_email['message'] = str_replace( '###OLD_EMAIL###', $old_email, $email_change_email['message'] );
  2454. $email_change_email['message'] = str_replace( '###NEW_EMAIL###', $new_email, $email_change_email['message'] );
  2455. $email_change_email['message'] = str_replace( '###SITENAME###', $network_name, $email_change_email['message'] );
  2456. $email_change_email['message'] = str_replace( '###SITEURL###', home_url(), $email_change_email['message'] );
  2457. wp_mail(
  2458. $email_change_email['to'],
  2459. sprintf(
  2460. $email_change_email['subject'],
  2461. $network_name
  2462. ),
  2463. $email_change_email['message'],
  2464. $email_change_email['headers']
  2465. );
  2466. }