# 28 — User Privacy Improvements

> **Goal**: Give users meaningful control over their data visibility, align with GDPR expectations, and build trust on the platform.

---

## Current State

### What Already Exists
- **Profile visibility toggle**: `publicProfile` (boolean) — all-or-nothing, hides entire profile
- **Post visibility**: 3 levels — `public`, `followers`, `private`
- **Reading list visibility**: `private`, `team`, `public`
- **Chat blocking**: per-conversation blocking (block/unblock user in DMs)
- **Newsletter preferences**: 4 toggles (recommendation, digest, follow notifications, message notifications)
- **Community privacy**: `public` flag on communities

### What's Missing
- No granular field-level visibility (bio, employment, interests, social links — all public or all hidden)
- No account deletion or data export (GDPR)
- No search engine visibility control
- No activity visibility controls (profile visitors, online status)
- No follower approval / follow request system
- No user-level blocking outside of DMs (can't block someone from seeing your posts/profile)
- No "who can message me" setting
- Visitor tracking has no opt-out

---

## Plan

### Phase 1 — Privacy Settings Page (Priority: High)

Create a dedicated `/settings/privacy` page with grouped controls.

#### 1.1 Profile Visibility Controls
- **Profile visibility**: `public` | `followers_only` | `private` (upgrade from boolean to enum)
- **Field-level toggles** (show/hide individually):
  - Bio / About me
  - Work / Education info
  - Social links (Facebook, LinkedIn, Google Scholar)
  - Research interests / Study field
  - Follower & following counts
  - Profile visitor count

**Implementation**:
- Add columns to `fos_user`: `profile_visibility` (enum), `show_bio`, `show_employment`, `show_social_links`, `show_interests`, `show_follower_count`, `show_visitor_count` (all boolean, default true)
- Create `PrivacySettingsController` with form
- Update `ProfileController::profileShow()` to respect field-level flags
- Update profile templates to conditionally render sections

#### 1.2 Activity & Online Status
- **Show online status**: toggle (default: on)
- **Show profile visitors count**: toggle (default: on)
- **Allow profile visit tracking**: toggle — if off, visits to this user's profile are not logged

**Implementation**:
- Add `show_online_status`, `allow_visit_tracking` to User entity
- Update visitor tracking logic in `ProfileController`

---

### Phase 2 — Social Privacy Controls (Priority: High)

#### 2.1 Who Can Message Me
- Options: `everyone` | `followers_only` | `nobody`
- Default: `everyone`

**Implementation**:
- Add `message_privacy` field to User entity
- Check in `ChatController` before allowing new conversation creation
- Show "This user doesn't accept messages" in UI

#### 2.2 Who Can See My Posts
- Default post visibility setting (so users don't have to pick each time)
- Options: `public` | `followers` | `private`

**Implementation**:
- Add `default_post_visibility` to User entity
- Pre-select in post creation form

#### 2.3 User-Level Blocking
- Block a user globally (not just in chat): blocked users cannot:
  - View your profile
  - See your posts in feed
  - Follow you
  - Send you messages
- Block list management page at `/settings/blocked-users`

**Implementation**:
- Create `UserBlock` entity (`blocker_id`, `blocked_id`, `created_at`)
- Create `UserBlockController` with block/unblock actions
- Add checks in `ProfileController`, `ProfilePostController`, `SocialFeedController`, `ChatController`
- Add "Block user" option to profile page and post menus

---

### Phase 3 — Follow Approval (Priority: Medium)

#### 3.1 Follow Request System
- Toggle: "Approve followers manually" (default: off)
- When enabled, follows become requests that need approval
- Pending requests shown in notifications

**Implementation**:
- Add `require_follow_approval` boolean to User entity
- Add `status` field to the follow relationship (`accepted` | `pending` | `rejected`)
- Create notification for follow requests
- Update follower count queries to only count accepted follows

---

### Phase 4 — Search & Indexing Privacy (Priority: Medium)

#### 4.1 Search Engine Visibility
- Toggle: "Allow search engines to index my profile" (default: on)
- When off, add `<meta name="robots" content="noindex, nofollow">` to profile page
- Exclude from sitemap

#### 4.2 Internal Search Visibility
- Toggle: "Show my profile in Shamra search results" (default: on)

**Implementation**:
- Add `searchable` boolean to User entity
- Add meta tag logic in profile template
- Filter user search queries

---

### Phase 5 — GDPR Compliance (Priority: High, Legal)

#### 5.1 Data Export
- "Download my data" button → generates JSON/ZIP with:
  - Profile info
  - Posts and comments
  - Reading lists
  - Chat messages
  - Follow relationships
  - Notification history
- Rate-limited: 1 export per 24 hours
- Async generation via Messenger, email download link when ready

**Implementation**:
- Create `DataExportController` with request endpoint
- Create `GenerateDataExport` message + handler
- Create `DataExport` entity to track export jobs
- Generate ZIP, store temporarily, email user

#### 5.2 Account Deletion
- "Delete my account" option with:
  - Password confirmation required
  - 30-day grace period (soft delete, can cancel)
  - Email confirmation of deletion request
  - After 30 days: anonymize data, delete PII, keep research contributions with "Deleted User" attribution
- Immediate actions on request: deactivate profile, stop emails, log out all sessions

**Implementation**:
- Add `deletion_requested_at` field to User entity
- Create `AccountDeletionController`
- Create scheduled command `app:process-account-deletions` (runs daily)
- Anonymization: replace name/email/bio with placeholders, delete avatar, keep posts attributed to "مستخدم محذوف"

#### 5.3 Privacy Policy & Consent
- Link to privacy policy in registration flow
- Cookie consent banner (if not already present)
- Consent tracking for newsletter/email preferences

---

### Phase 6 — Privacy Indicators in UI (Priority: Low)

#### 6.1 Visual Privacy Cues
- Lock icon on private profiles
- Eye icon showing post visibility level
- "Only you can see this" label on private content
- Privacy summary card on settings page showing current exposure level

#### 6.2 Privacy Health Score
- Simple score (e.g., "Your privacy level: Medium")
- Suggestions: "Your email is visible to everyone — consider hiding it"
- Shown on privacy settings page

---

## Database Changes Summary

```sql
-- Phase 1
ALTER TABLE fos_user ADD profile_visibility VARCHAR(20) DEFAULT 'public';
ALTER TABLE fos_user ADD show_bio TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD show_employment TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD show_social_links TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD show_interests TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD show_follower_count TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD show_visitor_count TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD show_online_status TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD allow_visit_tracking TINYINT(1) DEFAULT 1;

-- Phase 2
ALTER TABLE fos_user ADD message_privacy VARCHAR(20) DEFAULT 'everyone';
ALTER TABLE fos_user ADD default_post_visibility VARCHAR(20) DEFAULT 'public';

CREATE TABLE user_block (
    id INT AUTO_INCREMENT PRIMARY KEY,
    blocker_id INT NOT NULL,
    blocked_id INT NOT NULL,
    created_at DATETIME NOT NULL,
    UNIQUE KEY unique_block (blocker_id, blocked_id),
    FOREIGN KEY (blocker_id) REFERENCES fos_user(id),
    FOREIGN KEY (blocked_id) REFERENCES fos_user(id)
) COLLATE utf8mb4_unicode_ci;

-- Phase 3
ALTER TABLE fos_user ADD require_follow_approval TINYINT(1) DEFAULT 0;
-- (follow status field depends on current follow entity structure)

-- Phase 4
ALTER TABLE fos_user ADD search_engine_indexable TINYINT(1) DEFAULT 1;
ALTER TABLE fos_user ADD searchable TINYINT(1) DEFAULT 1;

-- Phase 5
ALTER TABLE fos_user ADD deletion_requested_at DATETIME DEFAULT NULL;

CREATE TABLE data_export (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    status VARCHAR(20) DEFAULT 'pending',
    file_path VARCHAR(500) DEFAULT NULL,
    requested_at DATETIME NOT NULL,
    completed_at DATETIME DEFAULT NULL,
    expires_at DATETIME DEFAULT NULL,
    FOREIGN KEY (user_id) REFERENCES fos_user(id)
) COLLATE utf8mb4_unicode_ci;
```

## New Files

| File | Purpose |
|------|---------|
| `src/Controller/PrivacySettingsController.php` | Privacy settings form |
| `src/Controller/UserBlockController.php` | Block/unblock actions |
| `src/Controller/DataExportController.php` | GDPR data export |
| `src/Controller/AccountDeletionController.php` | Account deletion flow |
| `src/Entity/UserBlock.php` | Block relationship entity |
| `src/Entity/DataExport.php` | Export job tracking |
| `src/Message/GenerateDataExport.php` | Async export message |
| `src/MessageHandler/GenerateDataExportHandler.php` | Export builder |
| `src/Command/ProcessAccountDeletionsCommand.php` | Daily cleanup |
| `templates/settings/privacy.html.twig` | Privacy settings page |
| `templates/settings/blocked_users.html.twig` | Block list management |
| `templates/settings/delete_account.html.twig` | Deletion confirmation |
| `translations/Privacy.ar.yml` | Arabic translations |
| `translations/Privacy.en.yml` | English translations |

## Priority Order

1. **Phase 1 + Phase 5.2** — Profile visibility controls + account deletion (highest user impact + legal)
2. **Phase 2** — Social privacy controls (blocking, messaging restrictions)
3. **Phase 5.1** — Data export
4. **Phase 3** — Follow approval
5. **Phase 4 + Phase 6** — Search visibility + UI indicators
