Keep profile photos and fields in line with Microsoft 365 on every run
Some checks are pending
CI / PHP lint (7.4) (pull_request) Waiting to run
CI / PHP lint (8.0) (pull_request) Waiting to run
CI / PHP lint (8.1) (pull_request) Waiting to run
CI / PHP lint (8.2) (pull_request) Waiting to run
CI / PHP lint (8.3) (pull_request) Waiting to run
CI / PHP lint (8.4) (pull_request) Waiting to run
CI / WordPress Coding Standards (pull_request) Waiting to run
CI / WordPress.org Plugin Check (pull_request) Waiting to run

- Photo versions are compared on every sync run via Graph $batch
  (20 users per request); only changed photos are downloaded, the old
  file is deleted and the avatar URL changes. Photos deleted in
  Microsoft 365 are deleted in WordPress. Graph errors never delete a
  photo. Download limit per run (500) with deferral to the next run.
- Switching the photo sync off removes all stored photos; deselected
  m365_* profile fields are removed from the profiles.
- A user's photo is deleted together with the user (delete_user hook).
- Dry run reports photo changes without downloading.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Friederich Loheide 2026-09-23 16:40:45 +00:00
parent 4edf20bc45
commit 9f77e9027f
11 changed files with 589 additions and 339 deletions

View file

@ -640,7 +640,7 @@ class M365_Login_Admin {
</label>
<?php endforeach; ?>
</div>
<p class="description"><?php esc_html_e( 'Profile photos are stored in wp-content/uploads/m365-login-avatars/ and replace the Gravatar. They are checked about once a day per user.', 'm365-login' ); ?></p>
<p class="description"><?php esc_html_e( 'Profile photos are stored in wp-content/uploads/m365-login-avatars/ and replace the Gravatar. They are compared on every run: changed photos are downloaded again, photos deleted in Microsoft 365 are deleted in WordPress too. Fields and photos you deselect here are removed from the profiles on the next run (first and last name and display name stay).', 'm365-login' ); ?></p>
</div>
<div class="m365-card">