Няма описание

avatar.php 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  1. <?php
  2. class NextendSocialLoginAvatar {
  3. /**
  4. * @return NextendSocialLoginAvatar
  5. */
  6. public static function getInstance() {
  7. static $inst = null;
  8. if ($inst === null) {
  9. $inst = new self();
  10. }
  11. return $inst;
  12. }
  13. public function __construct() {
  14. if (NextendSocialLogin::$settings->get('avatar_store')) {
  15. add_action('nsl_update_avatar', array(
  16. $this,
  17. 'updateAvatar'
  18. ), 10, 3);
  19. // WP User Avatar https://wordpress.org/plugins/wp-user-avatar/
  20. // Ultimate member
  21. if (!defined('WPUA_VERSION') && !class_exists('UM', false) && !class_exists('buddypress', false)) {
  22. add_filter('pre_get_avatar_data', array(
  23. $this,
  24. 'preGetAvatarData'
  25. ), 1, 2);
  26. }
  27. add_filter('post_mime_types', array(
  28. $this,
  29. 'addPostMimeTypeAvatar'
  30. ));
  31. add_filter('ajax_query_attachments_args', array(
  32. $this,
  33. 'modifyQueryAttachmentsArgs'
  34. ));
  35. }
  36. }
  37. public function addPostMimeTypeAvatar($types) {
  38. $types['avatar'] = array(
  39. __('Avatar', 'nextend-facebook-connect'),
  40. __('Manage Avatar', 'nextend-facebook-connect'),
  41. _n_noop('Avatar <span class="count">(%s)</span>', 'Avatar <span class="count">(%s)</span>', 'nextend-facebook-connect')
  42. );
  43. return $types;
  44. }
  45. public function modifyQueryAttachmentsArgs($query) {
  46. if (!isset($query['meta_query']) || !is_array($query['meta_query'])) {
  47. $query['meta_query'] = array();
  48. }
  49. if (isset($query['post_mime_type']) && $query['post_mime_type'] === 'avatar') {
  50. $query['post_mime_type'] = 'image';
  51. $query['meta_query']['relation'] = 'AND';
  52. $query['meta_query'][] = array(
  53. 'key' => '_wp_attachment_wp_user_avatar',
  54. 'compare' => 'EXISTS'
  55. );
  56. } else {
  57. $avatars_in_all_media = NextendSocialLogin::$settings->get('avatars_in_all_media');
  58. //Avatars will be loaded in Media Libray Grid view - All media items if $avatars_in_all_media is disabled!
  59. if (!$avatars_in_all_media) {
  60. $query['meta_query']['relation'] = 'AND';
  61. $query['meta_query'][] = array(
  62. 'key' => '_wp_attachment_wp_user_avatar',
  63. 'compare' => 'NOT EXISTS'
  64. );
  65. }
  66. }
  67. return $query;
  68. }
  69. /**
  70. * @param NextendSocialProvider $provider
  71. * @param $user_id
  72. * @param $avatarUrl
  73. */
  74. public function updateAvatar($provider, $user_id, $avatarUrl) {
  75. global $blog_id, $wpdb;
  76. if (!empty($avatarUrl)) {
  77. if (class_exists('UM', false)) {
  78. require_once(ABSPATH . '/wp-admin/includes/file.php');
  79. $profile_photo = get_user_meta($user_id, 'profile_photo', true);
  80. if (empty($profile_photo)) {
  81. $extension = 'jpg';
  82. if (preg_match('/\.(jpg|jpeg|gif|png)/', $avatarUrl, $match)) {
  83. $extension = $match[1];
  84. }
  85. $avatarTempPath = self::download_url($avatarUrl);
  86. if (!is_wp_error($avatarTempPath)) {
  87. $umAvatarKey = 'profile_photo';
  88. $umNameWithExtension = $umAvatarKey . '.' . $extension;
  89. $umUserAvatarDir = UM()
  90. ->uploader()
  91. ->get_upload_user_base_dir($user_id, true);
  92. if ($umUserAvatarDir) {
  93. $umUserAvatarPath = $umUserAvatarDir . DIRECTORY_SEPARATOR . $umNameWithExtension;
  94. $umAvatarInfo = @getimagesize($avatarTempPath);
  95. /*this copy will be deleted after resizing*/
  96. copy($avatarTempPath, $umUserAvatarPath);
  97. UM()
  98. ->uploader()
  99. ->resize_image($umUserAvatarPath, $umUserAvatarPath, $umAvatarKey, $user_id, '0,0,' . $umAvatarInfo[0] . ',' . $umAvatarInfo[0]);
  100. /*the final profile_photo*/
  101. copy($avatarTempPath, $umUserAvatarPath);
  102. update_user_meta($user_id, $umAvatarKey, $umNameWithExtension);
  103. }
  104. }
  105. unlink($avatarTempPath);
  106. UM()
  107. ->user()
  108. ->remove_cache($user_id);
  109. };
  110. return;
  111. }
  112. //upload user avatar for BuddyPress - bp_displayed_user_avatar() function
  113. if (class_exists('BuddyPress', false)) {
  114. if (!empty($avatarUrl)) {
  115. $extension = 'jpg';
  116. if (preg_match('/\.(jpg|jpeg|gif|png)/', $avatarUrl, $match)) {
  117. $extension = $match[1];
  118. }
  119. require_once(ABSPATH . '/wp-admin/includes/file.php');
  120. $avatarTempPath = self::download_url($avatarUrl);
  121. if (!is_wp_error($avatarTempPath)) {
  122. if (!function_exists('bp_members_avatar_upload_dir')) {
  123. $bpMembersFunctionsPath = buddypress()->plugin_dir . '/bp-members/bp-members-functions.php';
  124. if (file_exists($bpMembersFunctionsPath)) {
  125. require_once($bpMembersFunctionsPath);
  126. }
  127. }
  128. if (function_exists('bp_members_avatar_upload_dir')) {
  129. $pathInfo = bp_members_avatar_upload_dir('avatars', $user_id);
  130. if (wp_mkdir_p($pathInfo['path'])) {
  131. if ($av_dir = opendir($pathInfo['path'] . '/')) {
  132. $hasAvatar = false;
  133. while (false !== ($avatar_file = readdir($av_dir))) {
  134. if ((preg_match("/-bpfull/", $avatar_file) || preg_match("/-bpthumb/", $avatar_file))) {
  135. $hasAvatar = true;
  136. break;
  137. }
  138. }
  139. if (!$hasAvatar) {
  140. copy($avatarTempPath, $pathInfo['path'] . '/' . 'avatar-bpfull.' . $extension);
  141. rename($avatarTempPath, $pathInfo['path'] . '/' . 'avatar-bpthumb.' . $extension);
  142. }
  143. }
  144. closedir($av_dir);
  145. }
  146. }
  147. }
  148. }
  149. }
  150. /**
  151. * $original_attachment_id is false, if the user has had avatar set but the path is not found.
  152. */
  153. $original_attachment_id = get_user_meta($user_id, $wpdb->get_blog_prefix($blog_id) . 'user_avatar', true);
  154. $original_attachment_md5 = false;
  155. if ($original_attachment_id) {
  156. $attached_file = get_attached_file($original_attachment_id);
  157. if (($attached_file && !file_exists($attached_file)) || !$attached_file) {
  158. $original_attachment_id = false;
  159. } else {
  160. /**
  161. * We should only get the md5 value of the image, if there is an existing attachment, indeed.
  162. */
  163. $original_attachment_md5 = get_user_meta($user_id, 'nsl_user_avatar_md5', true);
  164. }
  165. }
  166. $overwriteAttachment = false;
  167. /**
  168. * Overwrite the original attachment if avatar was set and the provider attachment exits.
  169. */
  170. if ($original_attachment_id && get_post_meta($original_attachment_id, $provider->getId() . '_avatar', true)) {
  171. $overwriteAttachment = true;
  172. }
  173. if (!$original_attachment_id) {
  174. /**
  175. * If the user unlink and link the social provider back the original avatar will be used.
  176. */
  177. $args = array(
  178. 'post_type' => 'attachment',
  179. 'post_status' => 'inherit',
  180. 'meta_query' => array(
  181. array(
  182. 'key' => $provider->getId() . '_avatar',
  183. 'value' => $provider->getAuthUserData('id')
  184. )
  185. )
  186. );
  187. $query = new WP_Query($args);
  188. if ($query->post_count > 0) {
  189. $original_attachment_id = $query->posts[0]->ID;
  190. $overwriteAttachment = true;
  191. update_user_meta($user_id, $wpdb->get_blog_prefix($blog_id) . 'user_avatar', $original_attachment_id);
  192. }
  193. }
  194. /**
  195. * If there was no original avatar or overwrite mode is on, download the avatar of the selected provider.*
  196. */
  197. if (!$original_attachment_id || $overwriteAttachment === true) {
  198. require_once(ABSPATH . '/wp-admin/includes/file.php');
  199. $avatarTempPath = self::download_url($avatarUrl);
  200. if (!is_wp_error($avatarTempPath)) {
  201. $mime = wp_get_image_mime($avatarTempPath);
  202. $mime_to_ext = apply_filters('getimagesize_mimes_to_exts', array(
  203. 'image/jpeg' => 'jpg',
  204. 'image/png' => 'png',
  205. 'image/gif' => 'gif',
  206. 'image/bmp' => 'bmp',
  207. 'image/tiff' => 'tif',
  208. ));
  209. /**
  210. * If the uploaded image has extension from the mime type and it is appear in the $mime_to_ext.
  211. * Make a unique filename, depending on the extension.
  212. * Copy the downloaded file with the new name to the uploads path.
  213. * Unlink the downloaded file.
  214. */
  215. if (isset($mime_to_ext[$mime])) {
  216. $wp_upload_dir = wp_upload_dir();
  217. /**
  218. * The name of the folder inside /wp-content/uploads where the user avatars will be uploaded.
  219. * Can be changed by defining the NSL_AVATARS_FOLDER constant.
  220. */
  221. $nslUploadDirName = 'nsl_avatars';
  222. if (defined('NSL_AVATARS_FOLDER')) {
  223. $nslUploadDirName = NSL_AVATARS_FOLDER;
  224. }
  225. $nslUploadDir = trailingslashit($wp_upload_dir['basedir']) . $nslUploadDirName;
  226. if (wp_mkdir_p($nslUploadDir)) {
  227. $filename = wp_hash($user_id) . '.' . $mime_to_ext[$mime];
  228. $filename = wp_unique_filename($nslUploadDir, $filename);
  229. $newAvatarPath = trailingslashit($nslUploadDir) . $filename;
  230. $newFile = @copy($avatarTempPath, $newAvatarPath);
  231. @unlink($avatarTempPath);
  232. if (false !== $newFile) {
  233. $url = $wp_upload_dir['baseurl'] . '/' . $nslUploadDirName . '/' . basename($filename);
  234. $newAvatarMD5 = md5_file($newAvatarPath);
  235. if ($overwriteAttachment) {
  236. $originalAvatarImage = get_attached_file($original_attachment_id);
  237. // we got the same image, so we do not want to store it
  238. if ($original_attachment_md5 === $newAvatarMD5) {
  239. @unlink($newAvatarPath);
  240. } else {
  241. // Store the new avatar and remove the old one
  242. @unlink($originalAvatarImage);
  243. foreach (get_intermediate_image_sizes() as $size) {
  244. /**
  245. * Delete the previous Avatar sub-sizes to avoid orphan images
  246. */
  247. $originalAvatarSubsize = image_get_intermediate_size($original_attachment_id, $size);
  248. if (isset($originalAvatarSubsize['path'])) {
  249. $originalAvatarSubsizePath = trailingslashit($wp_upload_dir['basedir']) . $originalAvatarSubsize['path'];
  250. if (file_exists($originalAvatarSubsizePath)) {
  251. @unlink($originalAvatarSubsizePath);
  252. }
  253. }
  254. }
  255. update_attached_file($original_attachment_id, $newAvatarPath);
  256. // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
  257. require_once(ABSPATH . 'wp-admin/includes/image.php');
  258. wp_update_attachment_metadata($original_attachment_id, wp_generate_attachment_metadata($original_attachment_id, $newAvatarPath));
  259. update_user_meta($user_id, $wpdb->get_blog_prefix($blog_id) . 'user_avatar', $original_attachment_id);
  260. update_user_meta($user_id, 'nsl_user_avatar_md5', $newAvatarMD5);
  261. }
  262. } else {
  263. $attachment = array(
  264. 'guid' => $url,
  265. 'post_mime_type' => $mime,
  266. 'post_title' => '',
  267. 'post_content' => '',
  268. 'post_status' => 'private',
  269. );
  270. $new_attachment_id = wp_insert_attachment($attachment, $newAvatarPath);
  271. if (!is_wp_error($new_attachment_id)) {
  272. // Make sure that this file is included, as wp_generate_attachment_metadata() depends on it.
  273. require_once(ABSPATH . 'wp-admin/includes/image.php');
  274. wp_update_attachment_metadata($new_attachment_id, wp_generate_attachment_metadata($new_attachment_id, $newAvatarPath));
  275. update_post_meta($new_attachment_id, $provider->getId() . '_avatar', $provider->getAuthUserData('id'));
  276. update_post_meta($new_attachment_id, '_wp_attachment_wp_user_avatar', $user_id);
  277. update_user_meta($user_id, $wpdb->get_blog_prefix($blog_id) . 'user_avatar', $new_attachment_id);
  278. update_user_meta($user_id, 'nsl_user_avatar_md5', $newAvatarMD5);
  279. }
  280. }
  281. }
  282. }
  283. }
  284. }
  285. }
  286. }
  287. }
  288. public function preGetAvatarData($args, $id_or_email) {
  289. global $blog_id, $wpdb;
  290. $id = NextendSocialLogin::getUserIDByIdOrEmail($id_or_email);
  291. if ($id == 0) {
  292. return $args;
  293. }
  294. /**
  295. * Get the avatar attachment id of the user.
  296. */
  297. $attachment_id = get_user_meta($id, $wpdb->get_blog_prefix($blog_id) . 'user_avatar', true);
  298. if (wp_attachment_is_image($attachment_id)) {
  299. $image_src_array = wp_get_attachment_image_src($attachment_id);
  300. if (isset($args['size'])) {
  301. $get_size = is_numeric($args['size']) ? array(
  302. $args['size'],
  303. $args['size']
  304. ) : $args['size'];
  305. $image_src_array = wp_get_attachment_image_src($attachment_id, $get_size);
  306. }
  307. $args['url'] = $image_src_array[0];
  308. }
  309. return $args;
  310. }
  311. public function removeProfilePictureGravatarDescription($description) {
  312. if (strpos($description, 'Gravatar') !== false) {
  313. return '';
  314. }
  315. return $description;
  316. }
  317. /**
  318. * Adjusted according to WordPress 5.7
  319. * Override for WordPress default download_url() since wp_tempnam() can not handle long urls properly to generate temp file names.
  320. *
  321. * Downloads a URL to a local temporary file using the WordPress HTTP API.
  322. *
  323. * Please note that the calling function must unlink() the file.
  324. *
  325. * @param string $url The URL of the file to download.
  326. * @param int $timeout The timeout for the request to download the file.
  327. * Default 300 seconds.
  328. * @param bool $signature_verification Whether to perform Signature Verification.
  329. * Default false.
  330. *
  331. * @return string|WP_Error Filename on success, WP_Error on failure.
  332. * @since 2.5.0
  333. * @since 5.2.0 Signature Verification with SoftFail was added.
  334. *
  335. */
  336. public static function download_url($url, $timeout = 300, $signature_verification = false) {
  337. // WARNING: The file is not automatically deleted, the script must unlink() the file.
  338. if (!$url) {
  339. return new WP_Error('http_no_url', __('Invalid URL Provided.'));
  340. }
  341. $tmpfname = wp_tempnam();
  342. if (!$tmpfname) {
  343. return new WP_Error('http_no_file', __('Could not create Temporary file.'));
  344. }
  345. $response = wp_safe_remote_get($url, array(
  346. 'timeout' => $timeout,
  347. 'stream' => true,
  348. 'filename' => $tmpfname,
  349. ));
  350. if (is_wp_error($response)) {
  351. unlink($tmpfname);
  352. return $response;
  353. }
  354. $response_code = wp_remote_retrieve_response_code($response);
  355. if (200 != $response_code) {
  356. $data = array(
  357. 'code' => $response_code,
  358. );
  359. // Retrieve a sample of the response body for debugging purposes.
  360. $tmpf = fopen($tmpfname, 'rb');
  361. if ($tmpf) {
  362. /**
  363. * Filters the maximum error response body size in `download_url()`.
  364. *
  365. * @param int $size The maximum error response body size. Default 1 KB.
  366. *
  367. * @see download_url()
  368. *
  369. * @since 5.1.0
  370. *
  371. */
  372. $response_size = apply_filters('download_url_error_max_body_size', KB_IN_BYTES);
  373. $data['body'] = fread($tmpf, $response_size);
  374. fclose($tmpf);
  375. }
  376. unlink($tmpfname);
  377. return new WP_Error('http_404', trim(wp_remote_retrieve_response_message($response)), $data);
  378. }
  379. $content_md5 = wp_remote_retrieve_header($response, 'content-md5');
  380. if ($content_md5) {
  381. $md5_check = verify_file_md5($tmpfname, $content_md5);
  382. if (is_wp_error($md5_check)) {
  383. unlink($tmpfname);
  384. return $md5_check;
  385. }
  386. }
  387. // If the caller expects signature verification to occur, check to see if this URL supports it.
  388. if ($signature_verification) {
  389. /**
  390. * Filters the list of hosts which should have Signature Verification attempted on.
  391. *
  392. * @param string[] $hostnames List of hostnames.
  393. *
  394. * @since 5.2.0
  395. *
  396. */
  397. $signed_hostnames = apply_filters('wp_signature_hosts', array(
  398. 'wordpress.org',
  399. 'downloads.wordpress.org',
  400. 's.w.org'
  401. ));
  402. $signature_verification = in_array(parse_url($url, PHP_URL_HOST), $signed_hostnames, true);
  403. }
  404. // Perform signature valiation if supported.
  405. if ($signature_verification) {
  406. $signature = wp_remote_retrieve_header($response, 'x-content-signature');
  407. if (!$signature) {
  408. // Retrieve signatures from a file if the header wasn't included.
  409. // WordPress.org stores signatures at $package_url.sig.
  410. $signature_url = false;
  411. $url_path = parse_url($url, PHP_URL_PATH);
  412. if ('.zip' === substr($url_path, -4) || '.tar.gz' === substr($url_path, -7)) {
  413. $signature_url = str_replace($url_path, $url_path . '.sig', $url);
  414. }
  415. /**
  416. * Filters the URL where the signature for a file is located.
  417. *
  418. * @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known.
  419. * @param string $url The URL being verified.
  420. *
  421. * @since 5.2.0
  422. *
  423. */
  424. $signature_url = apply_filters('wp_signature_url', $signature_url, $url);
  425. if ($signature_url) {
  426. $signature_request = wp_safe_remote_get($signature_url, array(
  427. 'limit_response_size' => 10 * KB_IN_BYTES,
  428. // 10KB should be large enough for quite a few signatures.
  429. ));
  430. if (!is_wp_error($signature_request) && 200 === wp_remote_retrieve_response_code($signature_request)) {
  431. $signature = explode("\n", wp_remote_retrieve_body($signature_request));
  432. }
  433. }
  434. }
  435. // Perform the checks.
  436. $signature_verification = verify_file_signature($tmpfname, $signature, basename(parse_url($url, PHP_URL_PATH)));
  437. }
  438. if (is_wp_error($signature_verification)) {
  439. if (/**
  440. * Filters whether Signature Verification failures should be allowed to soft fail.
  441. *
  442. * WARNING: This may be removed from a future release.
  443. *
  444. * @param bool $signature_softfail If a softfail is allowed.
  445. * @param string $url The url being accessed.
  446. *
  447. * @since 5.2.0
  448. *
  449. */ apply_filters('wp_signature_softfail', true, $url)) {
  450. $signature_verification->add_data($tmpfname, 'softfail-filename');
  451. } else {
  452. // Hard-fail.
  453. unlink($tmpfname);
  454. }
  455. return $signature_verification;
  456. }
  457. return $tmpfname;
  458. }
  459. }
  460. NextendSocialLoginAvatar::getInstance();