feat: Add customer avatar upload and product downloadable files

Customer Avatar Upload:
- Add /account/avatar endpoint for upload/delete
- Add /account/avatar-settings endpoint for settings
- Update AccountDetails.tsx with avatar upload UI
- Support base64 image upload with validation

Product Downloadable Files:
- Create DownloadsTab component for file management
- Add downloads state to ProductFormTabbed
- Show Downloads tab when 'downloadable' is checked
- Support file name, URL, download limit, and expiry
This commit is contained in:
Dwindi Ramadhana
2026-01-05 00:05:18 +07:00
parent 6c8cbb93e6
commit 51c759a4f5
4 changed files with 549 additions and 27 deletions

View File

@@ -87,6 +87,27 @@ class AccountController {
'callback' => [__CLASS__, 'get_downloads'],
'permission_callback' => [__CLASS__, 'check_customer_permission'],
]);
// Avatar upload
register_rest_route($namespace, '/account/avatar', [
[
'methods' => 'POST',
'callback' => [__CLASS__, 'upload_avatar'],
'permission_callback' => [__CLASS__, 'check_customer_permission'],
],
[
'methods' => 'DELETE',
'callback' => [__CLASS__, 'delete_avatar'],
'permission_callback' => [__CLASS__, 'check_customer_permission'],
],
]);
// Get avatar settings (check if custom avatars are enabled)
register_rest_route($namespace, '/account/avatar-settings', [
'methods' => 'GET',
'callback' => [__CLASS__, 'get_avatar_settings'],
'permission_callback' => [__CLASS__, 'check_customer_permission'],
]);
}
/**
@@ -209,6 +230,143 @@ class AccountController {
], 200);
}
/**
* Upload customer avatar
*/
public static function upload_avatar(WP_REST_Request $request) {
// Check if custom avatars are enabled
$settings = get_option('woonoow_customer_settings', []);
$allow_custom_avatar = $settings['allow_custom_avatar'] ?? false;
if (!$allow_custom_avatar) {
return new WP_Error('avatar_disabled', 'Custom avatars are not enabled', ['status' => 403]);
}
$user_id = get_current_user_id();
// Check for file data (base64 or URL)
$avatar_data = $request->get_param('avatar');
$avatar_url = $request->get_param('avatar_url');
if ($avatar_url) {
// Avatar URL provided (from media library)
update_user_meta($user_id, 'woonoow_custom_avatar', esc_url_raw($avatar_url));
return new WP_REST_Response([
'success' => true,
'message' => 'Avatar updated successfully',
'avatar_url' => $avatar_url,
], 200);
}
if (!$avatar_data) {
return new WP_Error('no_avatar', 'No avatar data provided', ['status' => 400]);
}
// Handle base64 image upload
if (strpos($avatar_data, 'data:image') === 0) {
// Extract base64 data
$parts = explode(',', $avatar_data);
if (count($parts) !== 2) {
return new WP_Error('invalid_data', 'Invalid image data format', ['status' => 400]);
}
$image_data = base64_decode($parts[1]);
// Determine file extension from mime type
preg_match('/data:image\/(\w+);/', $parts[0], $matches);
$extension = $matches[1] ?? 'png';
// Validate extension
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (!in_array(strtolower($extension), $allowed)) {
return new WP_Error('invalid_type', 'Invalid image type. Allowed: jpg, png, gif, webp', ['status' => 400]);
}
// Create upload directory
$upload_dir = wp_upload_dir();
$avatar_dir = $upload_dir['basedir'] . '/woonoow-avatars';
if (!file_exists($avatar_dir)) {
wp_mkdir_p($avatar_dir);
}
// Generate unique filename
$filename = 'avatar-' . $user_id . '-' . time() . '.' . $extension;
$filepath = $avatar_dir . '/' . $filename;
// Delete old avatar if exists
$old_avatar = get_user_meta($user_id, 'woonoow_custom_avatar', true);
if ($old_avatar) {
$old_path = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $old_avatar);
if (file_exists($old_path)) {
unlink($old_path);
}
}
// Save new avatar
if (file_put_contents($filepath, $image_data) === false) {
return new WP_Error('upload_failed', 'Failed to save avatar', ['status' => 500]);
}
// Get URL
$avatar_url = $upload_dir['baseurl'] . '/woonoow-avatars/' . $filename;
// Save to user meta
update_user_meta($user_id, 'woonoow_custom_avatar', $avatar_url);
return new WP_REST_Response([
'success' => true,
'message' => 'Avatar uploaded successfully',
'avatar_url' => $avatar_url,
], 200);
}
return new WP_Error('invalid_data', 'Invalid avatar data', ['status' => 400]);
}
/**
* Delete customer avatar
*/
public static function delete_avatar(WP_REST_Request $request) {
$user_id = get_current_user_id();
// Get current avatar
$avatar_url = get_user_meta($user_id, 'woonoow_custom_avatar', true);
if ($avatar_url) {
// Try to delete the file
$upload_dir = wp_upload_dir();
$filepath = str_replace($upload_dir['baseurl'], $upload_dir['basedir'], $avatar_url);
if (file_exists($filepath)) {
unlink($filepath);
}
// Remove from user meta
delete_user_meta($user_id, 'woonoow_custom_avatar');
}
return new WP_REST_Response([
'success' => true,
'message' => 'Avatar removed successfully',
], 200);
}
/**
* Get avatar settings
*/
public static function get_avatar_settings(WP_REST_Request $request) {
$user_id = get_current_user_id();
$settings = get_option('woonoow_customer_settings', []);
return new WP_REST_Response([
'allow_custom_avatar' => $settings['allow_custom_avatar'] ?? false,
'current_avatar' => get_user_meta($user_id, 'woonoow_custom_avatar', true) ?: null,
'gravatar_url' => get_avatar_url($user_id),
], 200);
}
/**
* Update password
*/