Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
671cf4a
Adds method to submit form after selecting an image for upload
betsyecastro Nov 11, 2024
5a62b1b
🚸 Refactor profile picture upload process to use a Livewire component
betsyecastro Nov 19, 2024
3bff28d
Add style for edit button position
betsyecastro Nov 20, 2024
71a1561
Add authorization checks for profile editing and updates
betsyecastro Nov 20, 2024
347cda0
Add Livewire component to edit banner image
betsyecastro Nov 27, 2024
972fa2b
Validate banner image only when it has changed
betsyecastro Dec 11, 2024
7580a6e
Fix edit banner button position and dynamic banner image height
betsyecastro Dec 11, 2024
cd1886b
Reposition error messages in profile picture modal and banner image m…
betsyecastro Dec 11, 2024
3a63566
Combine profile and cover image editors into a single modal interface
betsyecastro Jan 10, 2025
eb9d8ec
Remove profile and cover image upload options from edit view
betsyecastro Jan 13, 2025
042adb7
Remove default value for banner image
betsyecastro Jan 21, 2025
e3fa7c3
Add ImagePicker generic component to handle image uploads
betsyecastro Feb 4, 2025
d6e0c4e
Redesign profile header card placeholders to ensure they're responsive
betsyecastro Feb 4, 2025
5e9e586
Move all the image uploading functionalities to the ImagePicker compo…
betsyecastro Feb 20, 2025
9840473
Add 'Select Image' H2 tag
betsyecastro Feb 20, 2025
0c81cf1
Add authorization to image picker component
betsyecastro Feb 28, 2025
4509670
Add redirect route as a component property
betsyecastro Feb 28, 2025
13d732c
Validate save and callback functions only if they are set
betsyecastro Feb 28, 2025
a3fde80
Adds ImagePicker feature test
betsyecastro Feb 28, 2025
bfd25f8
Adds docblocks to ImagePicker component
betsyecastro Feb 28, 2025
854b567
Fixes psalm errors
betsyecastro Mar 6, 2025
f95b7d3
Removes image and banner edit tests from ProfileTest and removes 'req…
betsyecastro Mar 7, 2025
f5b11ed
Removes methods to update image and banner from ProfilesController
betsyecastro Mar 7, 2025
5e9b14c
Fixes style of 'Edit' button in avatar layout
betsyecastro Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions app/Http/Controllers/ProfilesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,19 +270,6 @@ public function update(Profile $profile, string $section, ProfileUpdateRequest $
return redirect()->route('profiles.show', $profile->slug)->with('flash_message', 'Profile updated.');
}

public function updateImage(Profile $profile, ProfileImageRequest $request): RedirectResponse
{
return redirect()->route('profiles.edit', [$profile->slug, 'information'])->with('flash_message', $profile->processImage($request->file('image'), 'images'));
}

/**
* Update a Profile's banner image
*/
public function updateBanner(Profile $profile, ProfileBannerImageRequest $request): RedirectResponse
{
return redirect()->route('profiles.edit', [$profile->slug, 'information'])->with('flash_message', $profile->processImage($request->file('banner_image'), 'banners'));
}

/**
* Confirm deletion of a profile
*/
Expand Down
185 changes: 185 additions & 0 deletions app/Http/Livewire/ImagePicker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<?php

namespace App\Http\Livewire;

use App\Http\Requests\Concerns\HasImageUploads;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Database\Eloquent\Model;
use Livewire\Component;
use Livewire\WithFileUploads;
use Livewire\TemporaryUploadedFile;

class ImagePicker extends Component
{
use WithFileUploads, HasImageUploads, AuthorizesRequests;

/**
* Uploaded image file
* @var TemporaryUploadedFile
*/
public $image;

/**
* Existing image URL (if the model already has an image)
* @var string|null
*/
public $existing_image_url;

/**
* A kebab-case unique identifier for each component
* @var string
* for the Livewire wire:key directive to identify the DOM elements when multiple instances of the component exist
* Example: "banner-img"
*/
public $custom_key;

/**
* Additional instructions for the image file selection
* @var string|null
* Example: "This photo will appear on your profile page and as your application profile image - please use a high-quality image (300x300 pixels or larger)."
*
*/
public $custom_msg;

/**
* Model instance that has an image is associated
* @var \Illuminate\Database\Eloquent\Model
*/
public $model;

/**
* Method on the model used to save the image
* @var string|null
*/
public $save_function;

/**
* Parameters passed to the save function
* @var array|null
*/
public $save_params;

/**
* The parameter name expected by the save function for the uploaded image
* @var string
*/
public $image_param_name;

/**
* Callback function to execute after saving the image
* @var string|null
*/
public $callback_function;

/**
* Parameters for the callback function
* @var array|null
*/
public $callback_params;

/**
* Additional blade partial view to display below the image preview
* @var string|null
*/
public $partial_view;

/**
* Route to redirect to after saving the image
* @var string
*/
public $redirect_route;

/**
* Flash message displayed after saving the image
* @var string|null
*/
public $message;

/**
* Authorization parameters for checking user permissions
* @var array $auth_params
*/
public $auth_params;

public function mount()
{
if (isset($this->save_function)) { // Validates save function only if set
$this->validateCallUserFunc('save_function');
}

if (isset($this->callback_function)) { // Validates callback function only if set
$this->validateCallUserFunc('callback_function');
}
}

/**
* Validates the image after a file has been selected and stores the updated image in the save function parameters for uploading
*/
public function updatedImage()
{
if ($this->image) {
$this->validate(
['image' => "nullable|{$this->uploadedImageRules()}"],
);

$this->save_params[$this->image_param_name] = $this->image;
}
}

public function save()
{
$this->authorize(...$this->auth_params);

// Calls the model's save function if an image is uploaded
if ($this->image) {
$this->message = call_user_func([$this->model, $this->save_function], ...$this->save_params ?? []);
}

// If a callback function is set, execute it (e.g., updating related records)
if (isset($this->callback_function)) {
call_user_func([$this->model, $this->callback_function], ...$this->callback_params ?? []);
}

return redirect($this->redirect_route)->with('flash_message', $this->message);
}

/**
* Validates that the provided method name exists on the model
* Prevents runtime errors if an invalid method is supplied
*
* @param string $function_name
* Name of the function to validate
*/
private function validateCallUserFunc($function_name)
{
$class_name = get_class($this->model);

$this->validate([
'model' => [
'required',
function ($attribute, $value, $fail) use ($class_name) {
if (!class_exists($class_name) || !is_subclass_of($class_name, Model::class)) {
$fail('Please contact the app admin.');
logger()->error("The class {$class_name} is not a valid Eloquent model.");
}
}
],

$function_name => [
function ($attribute, $value, $fail) use ($class_name) {
/** @var string|null $value */
if (!is_callable($value, true) || !method_exists($class_name, $value)) {
$fail('Please contact the app admin.');
logger()->error("The method {$value} does not exist in {$class_name}.");
}
}
],
]);
}

public function render()
{
return view('livewire.image-picker');
}
}

75 changes: 75 additions & 0 deletions app/Http/Livewire/ProfileHeaderEditorModal.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace App\Http\Livewire;

use App\Profile;
use Livewire\Component;
use Livewire\WithFileUploads;
use App\Http\Requests\Concerns\HasImageUploads;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class ProfileHeaderEditorModal extends Component
{

use WithFileUploads, HasImageUploads, AuthorizesRequests;

public $user;
public Profile $profile;
public $image;
public bool $fancy_header;
public bool $fancy_header_right;
public $avatar_settings;
public $cover_settings;

public function mount()
{
$message = "Profile layout has been updated.";

$this->fancy_header = $this->profile->hasFancyHeader();
$this->fancy_header_right = $this->profile->hasFancyHeaderRight();

$this->avatar_settings = [
'existing_image_url' => $this->profile->image_url,
'custom_key' => "profile-img",
'custom_msg' => "This photo will appear on your profile page and as your application profile image - please use a high-quality image (300x300 pixels or larger).",
'save_function' => 'processImage',
'save_params' => [
'collection' => 'images',
],
'image_param_name' => 'new_image',
'callback_function' => 'updateLayoutSettings',
'callback_params' => [
'fancy_header' => false,
'fancy_header_right' => $this->fancy_header_right,
],
'redirect_route' => route('profiles.show', $this->profile->slug),
'message' => $message,
'auth_params' => ['update', [Profile::class, $this->profile]],
];

$this->cover_settings = [
'existing_image_url' => $this->profile->banner_url,
'custom_key' => "banner-img",
'custom_msg' => "This will use a full-width header style - please use a high-quality image (1280 × 720 pixels or larger).",
'save_function' => 'processImage',
'save_params' => [
'collection' => 'banners',
],
'image_param_name' => 'new_image',
'callback_function' => 'updateLayoutSettings',
'callback_params' => [
'fancy_header' => true,
'fancy_header_right' => $this->fancy_header_right,
],
'redirect_route' => route('profiles.show', $this->profile->slug),
'message' => $message,
'partial_view' => 'livewire.partials._fancy-header-settings',
'auth_params' => ['update', [Profile::class, $this->profile]],
];
}

public function render()
{
return view('livewire.profile-header-editor-modal');
}
}
36 changes: 0 additions & 36 deletions app/Http/Requests/ProfileBannerImageRequest.php

This file was deleted.

36 changes: 0 additions & 36 deletions app/Http/Requests/ProfileImageRequest.php

This file was deleted.

2 changes: 0 additions & 2 deletions app/Http/Requests/ProfileUpdateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,6 @@ public function informationRules(): array
'data.*.data.tertiary_url' => 'nullable|url',
'data.*.data.orc_id' => 'nullable|regex:/^[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]$/',
'data.*.data.orc_id_managed' => 'required|boolean',
'data.*.data.fancy_header' => 'required|boolean',
'data.*.data.fancy_header_right' => 'required|boolean',
];
}

Expand Down
Loading