-
Notifications
You must be signed in to change notification settings - Fork 215
Fix profile links not showing for vendor staff #3040
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
4645f82
5ace902
f314a1f
5d79aea
33dbd5b
36978fc
ed15346
5f60de5
ef0aef1
22970a2
c629f2e
d01af02
408f819
8b04408
eaf5262
b4b3423
74b4c91
48b559f
29fa6fc
1dc4afb
53a76af
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| namespace WeDevs\Dokan\REST; | ||
|
|
||
| use WeDevs\Dokan\Traits\VendorAuthorizable; | ||
| use WeDevs\Dokan\Vendor\Vendor; | ||
| use WP_Error; | ||
| use WP_Query; | ||
|
|
@@ -18,6 +19,7 @@ | |
| * @author weDevs <[email protected]> | ||
| */ | ||
| class StoreController extends WP_REST_Controller { | ||
| use VendorAuthorizable; | ||
|
|
||
| /** | ||
| * Endpoint namespace | ||
|
|
@@ -62,7 +64,7 @@ public function register_routes() { | |
| 'description' => __( 'Unique identifier for the object.', 'dokan-lite' ), | ||
| 'type' => 'integer', | ||
| 'sanitize_callback' => 'absint', | ||
| 'validate_callback' => 'dokan_rest_validate_store_id', | ||
| 'validate_callback' => [ $this, 'validate_store_id' ], | ||
| ], | ||
| ], | ||
| [ | ||
|
|
@@ -304,16 +306,30 @@ public function get_stores( $request ) { | |
| /** | ||
| * Get singe store | ||
| * | ||
| * Public endpoint: Returns public data for all users/guests (respecting admin settings). | ||
| * Sensitive data is only returned for authorized users (vendor, vendor staff, or admin). | ||
| * | ||
| * For vendor staff accessing via their own ID, the vendor ID is resolved to show their vendor's store. | ||
| * Vendors and vendor staff attempting to access another vendor's store will be blocked (403). | ||
| * | ||
| * @since 1.0.0 | ||
| * | ||
| * @param $request | ||
| * | ||
| * @return WP_Error|WP_REST_Response | ||
| */ | ||
| public function get_store( $request ) { | ||
| $store_id = (int) $request['id']; | ||
| $requested_id = absint( $request->get_param( 'id' ) ); | ||
|
|
||
| $store = dokan()->vendor->get( $store_id ); | ||
| $store = dokan()->vendor->get( $requested_id ); | ||
|
|
||
| if ( ! $store || ! $store->get_id() ) { | ||
| return new WP_Error( | ||
| 'dokan_rest_store_not_found', | ||
| __( 'Store not found.', 'dokan-lite' ), | ||
| [ 'status' => 404 ] | ||
| ); | ||
| } | ||
|
|
||
| $stores_data = $this->prepare_item_for_response( $store, $request ); | ||
| $response = rest_ensure_response( $stores_data ); | ||
|
|
@@ -357,13 +373,12 @@ public function delete_store( $request ) { | |
| * @return bool | ||
| */ | ||
| public function update_store_permissions_check( $request ) { | ||
| if ( current_user_can( 'manage_woocommerce' ) ) { | ||
| return true; | ||
| } | ||
| $requested_id = absint( $request->get_param( 'id' ) ); | ||
|
|
||
| if ( current_user_can( 'dokandar' ) ) { | ||
| return dokan_get_current_user_id() === absint( $request->get_param( 'id' ) ); | ||
| } | ||
| // Resolve vendor ID: handles both vendor IDs and vendor staff IDs | ||
| $store_id = $this->get_vendor_id_for_user( $requested_id ); | ||
|
|
||
| return $this->can_access_vendor_store( $store_id ); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -376,17 +391,54 @@ public function update_store_permissions_check( $request ) { | |
| * @return WP_Error|WP_REST_Response | ||
| */ | ||
| public function update_store( $request ) { | ||
| $store = dokan()->vendor->get( (int) $request->get_param( 'id' ) ); | ||
| $requested_id = absint( $request->get_param( 'id' ) ); | ||
|
|
||
| $params = $request->get_params(); | ||
| $store_id = dokan()->vendor->update( $store->get_id(), $params ); | ||
| // Resolve vendor ID: handles both vendor IDs and vendor staff IDs | ||
| $store_id = $this->get_vendor_id_for_user( $requested_id ); | ||
|
|
||
| if ( is_wp_error( $store_id ) ) { | ||
| return new WP_Error( $store_id->get_error_code(), $store_id->get_error_message() ); | ||
| if ( ! $store_id ) { | ||
| return new WP_Error( | ||
| 'dokan_rest_store_not_found', | ||
| __( 'Store not found.', 'dokan-lite' ), | ||
| [ 'status' => 404 ] | ||
| ); | ||
| } | ||
|
|
||
| $store = dokan()->vendor->get( $store_id ); | ||
|
|
||
| if ( ! $store || ! $store->get_id() ) { | ||
| return new WP_Error( | ||
| 'dokan_rest_store_not_found', | ||
| __( 'Store not found.', 'dokan-lite' ), | ||
| [ 'status' => 404 ] | ||
| ); | ||
| } | ||
| if ( ! $this->can_access_vendor_store( $store->get_id() ) ) { | ||
| return new WP_Error( | ||
| 'dokan_rest_store_cannot_access', | ||
| __( 'You do not have permission to access this store.', 'dokan-lite' ), | ||
| [ 'status' => 403 ] | ||
| ); | ||
| } | ||
|
|
||
| $params = $request->get_params(); | ||
|
|
||
| $restricted_fields = $this->get_restricted_fields_for_update( $store, $request ); | ||
|
|
||
| foreach ( $restricted_fields as $field ) { | ||
| if ( isset( $params[ $field ] ) ) { | ||
| unset( $params[ $field ] ); | ||
| } | ||
| } | ||
|
|
||
| $updated_store_id = dokan()->vendor->update( $store->get_id(), $params ); | ||
|
|
||
| if ( is_wp_error( $updated_store_id ) ) { | ||
| return new WP_Error( $updated_store_id->get_error_code(), $updated_store_id->get_error_message() ); | ||
| } | ||
|
|
||
| $store = dokan()->vendor->get( $updated_store_id ); | ||
|
|
||
| do_action( 'dokan_rest_stores_update_store', $store, $request ); | ||
|
|
||
| $stores_data = $this->prepare_item_for_response( $store, $request ); | ||
|
|
@@ -395,6 +447,43 @@ public function update_store( $request ) { | |
| return $response; | ||
| } | ||
|
|
||
| /** | ||
| * Get restricted fields for store update based on user role. | ||
| * | ||
| * @since DOKAN_SINCE | ||
| * | ||
| * @param \WeDevs\Dokan\Vendor\Vendor $store Store object. | ||
| * @param \WP_REST_Request $request Request object. | ||
| * | ||
| * @return array Array of restricted field names. | ||
| */ | ||
| protected function get_restricted_fields_for_update( $store, $request ) { | ||
| $is_admin = current_user_can( 'manage_options' ); | ||
| $is_vendor = dokan_is_user_seller( get_current_user_id(), true ); | ||
| $restricted_fields = []; | ||
|
|
||
| if ( ! $is_admin && ! $is_vendor ) { | ||
| $staff_restricted_fields = [ | ||
| 'email', | ||
| 'password', | ||
| ]; | ||
| array_push( $restricted_fields, ...$staff_restricted_fields ); | ||
| } | ||
|
|
||
| if ( ! $is_admin ) { | ||
| $vendor_restricted_fields = [ | ||
| 'dokan_admin_percentage', | ||
| 'dokan_admin_percentage_type', | ||
| 'dokan_admin_additional_fee', | ||
| 'admin_category_commission', | ||
| ]; | ||
|
|
||
| array_push( $restricted_fields, ...$vendor_restricted_fields ); | ||
| } | ||
|
|
||
| return apply_filters( 'dokan_rest_store_restricted_fields_for_update', $restricted_fields, $store, $request ); | ||
| } | ||
|
|
||
| /** | ||
| * Create store | ||
| * | ||
|
|
@@ -623,28 +712,94 @@ public function get_total_review_count( $id, $post_type, $status ) { | |
| /** | ||
| * Prepare a single user output for response | ||
| * | ||
| * Public data is returned for all users/guests (respecting admin settings for hiding vendor info). | ||
| * Sensitive data is only returned for authorized users (vendor, vendor staff, or admin). | ||
| * | ||
| * @param Vendor $store | ||
| * @param WP_REST_Request $request Request object. | ||
| * @param array $additional_fields (optional) | ||
| * @param bool $is_authorized (optional) Whether the current user is authorized to view sensitive data. | ||
| * | ||
| * @return WP_REST_Response $response Response data. | ||
| */ | ||
| public function prepare_item_for_response( $store, $request, $additional_fields = [] ) { | ||
| $data = $store->to_array(); | ||
|
|
||
| $commission_settings = $store->get_commission_settings(); | ||
| $data['admin_category_commission'] = $commission_settings->get_category_commissions(); | ||
| $data['admin_commission'] = $commission_settings->get_percentage(); | ||
| $data['admin_additional_fee'] = $commission_settings->get_flat(); | ||
| $data['admin_commission_type'] = $commission_settings->get_type(); | ||
| $is_authorized = $this->can_access_vendor_store( $store->get_id() ); | ||
|
|
||
| if ( $is_authorized ) { | ||
| $data['admin_category_commission'] = $store->get_commission_settings()->get_category_commissions(); | ||
| $data['admin_commission'] = $store->get_commission_settings()->get_percentage(); | ||
| $data['admin_additional_fee'] = $store->get_commission_settings()->get_flat(); | ||
| $data['admin_commission_type'] = $store->get_commission_settings()->get_type(); | ||
| } | ||
|
|
||
| $restricted_fields = $this->get_restricted_fields_for_view( $store, $request ); | ||
|
|
||
| foreach ( $restricted_fields as $field ) { | ||
| unset( $data[ $field ] ); | ||
| } | ||
|
|
||
| $data = array_merge( $data, apply_filters( 'dokan_rest_store_additional_fields', $additional_fields, $store, $request ) ); | ||
| $data = array_merge( $data, apply_filters( 'dokan_rest_store_additional_fields', $additional_fields, $store, $request, $is_authorized ) ); | ||
| $response = rest_ensure_response( $data ); | ||
| $response->add_links( $this->prepare_links( $data, $request ) ); | ||
|
|
||
| return apply_filters( 'dokan_rest_prepare_store_item_for_response', $response ); | ||
| } | ||
|
|
||
| /** | ||
| * Get restricted fields for store view based on user authorization. | ||
| * | ||
| * Determines which fields should be hidden from the store data response based on: | ||
| * - User authorization status (authorized users see more data) | ||
| * - User role (vendor staff cannot see admin commission data) | ||
| * - Admin settings (for hiding vendor info like address, phone, email) | ||
| * - Vendor preferences (vendor can choose to hide email) | ||
| * | ||
| * @since DOKAN_SINCE | ||
| * | ||
| * @param \WeDevs\Dokan\Vendor\Vendor $store Store object. | ||
| * @param \WP_REST_Request $request Request object. | ||
| * | ||
| * @return array Array of restricted field names that should be removed from the response. | ||
| */ | ||
| protected function get_restricted_fields_for_view( $store, $request ) { | ||
| $restricted_fields = []; | ||
|
|
||
| $is_authorized = $this->can_access_vendor_store( $store->get_id() ); | ||
|
|
||
| // Restrict admin commission fields for unauthorized users and vendor staff | ||
| if ( ! $is_authorized || ( $is_authorized && $this->is_staff_only( get_current_user_id() ) ) ) { | ||
| $restricted_fields[] = 'admin_category_commission'; | ||
| $restricted_fields[] = 'admin_commission'; | ||
| $restricted_fields[] = 'admin_additional_fee'; | ||
| $restricted_fields[] = 'admin_commission_type'; | ||
| } | ||
|
|
||
| // Additional restrictions for unauthorized users (public access) | ||
| if ( ! $is_authorized ) { | ||
| // Respect admin settings for hiding vendor info | ||
| if ( dokan_is_vendor_info_hidden( 'address' ) ) { | ||
| $restricted_fields[] = 'address'; | ||
| } | ||
|
|
||
| if ( dokan_is_vendor_info_hidden( 'phone' ) ) { | ||
| $restricted_fields[] = 'phone'; | ||
| } | ||
|
|
||
| // Hide email if admin setting hides it OR vendor doesn't want to show it | ||
| if ( dokan_is_vendor_info_hidden( 'email' ) || ! $store->show_email() ) { | ||
| $restricted_fields[] = 'email'; | ||
| } | ||
|
|
||
| // Always hide sensitive payment and store status data from public | ||
| $restricted_fields[] = 'payment'; | ||
| $restricted_fields[] = 'enabled'; | ||
| } | ||
|
|
||
| return apply_filters( 'dokan_rest_store_restricted_fields_for_view', $restricted_fields, $store, $request ); | ||
| } | ||
|
|
||
| /** | ||
| * Prepare a single user output for response | ||
| * | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,12 +9,11 @@ | |
|
|
||
| /** | ||
| * StoreSettings API Controller | ||
| * | ||
| * @package dokan | ||
| * | ||
| * @author weDevs <[email protected]> | ||
| */ | ||
| class StoreSettingController extends WP_REST_Controller { | ||
| class StoreSettingController extends StoreController { | ||
| /** | ||
| * Endpoint namespace | ||
| * | ||
|
|
@@ -83,44 +82,31 @@ | |
| * @return WP_Error|\WP_REST_Response | ||
| */ | ||
| public function update_settings( $request ) { | ||
| $vendor = $this->get_vendor( $request ); | ||
| $params = $request->get_params(); | ||
| $store_id = dokan()->vendor->update( $vendor->get_id(), $params ); | ||
| $vendor_id = (int) $request->get_param( 'vendor_id' ); | ||
| $request->set_param( 'id', $vendor_id ); | ||
| $response = parent::update_store( $request ); | ||
|
|
||
| if ( is_wp_error( $store_id ) ) { | ||
| return new WP_Error( $store_id->get_error_code(), $store_id->get_error_message() ); | ||
| if ( is_wp_error( $response ) ) { | ||
| return $response; | ||
| } | ||
|
|
||
| $store = dokan()->vendor->get( $store_id ); | ||
| $store = dokan()->vendor->get( $vendor_id ); | ||
|
|
||
| do_action( 'dokan_rest_store_settings_after_update', $store, $request ); | ||
|
|
||
| $stores_data = $this->prepare_item_for_response( $store, $request ); | ||
| $response = rest_ensure_response( $stores_data ); | ||
|
|
||
| return $response; | ||
| } | ||
|
|
||
| /** | ||
| * @param $request | ||
| * @param \WP_REST_Request $request | ||
| * | ||
| * @return mixed|WP_Error|\WP_HTTP_Response|\WP_REST_Response | ||
| */ | ||
| public function get_settings( $request ) { | ||
| $vendor = $this->get_vendor( $request ); | ||
| $response = dokan_get_store_info( $vendor->id ); | ||
|
|
||
| $methods_data = dokan_get_container()->get( 'dashboard' )->templates->settings->get_seller_payment_methods( $vendor->get_id() ); | ||
|
|
||
| $response['bank_payment_required_fields'] = dokan_bank_payment_required_fields(); | ||
| $response['active_payment_methods'] = $methods_data['active_methods'] ?? []; | ||
| $response['connected_methods'] = $methods_data['connected_methods'] ?? []; | ||
| $response['disconnected_methods'] = $methods_data['disconnected_methods'] ?? []; | ||
| $response['withdraw_options'] = dokan_withdraw_get_methods(); | ||
| $response['fields_placeholders'] = dokan_bank_payment_fields_placeholders(); | ||
| $response['chargeable_methods'] = dokan_withdraw_get_chargeable_methods(); | ||
| $vendor_id = (int) $request->get_param( 'vendor_id' ); | ||
| $request->set_param( 'id', $vendor_id ); | ||
|
|
||
| return rest_ensure_response( $response ); | ||
| return parent::get_store( $request ); | ||
| } | ||
|
Comment on lines
105
to
110
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same edge case in Similar to 🔎 Proposed fix public function get_settings( $request ) {
$vendor_id = (int) $request->get_param( 'vendor_id' );
+
+ // Fall back to current user's vendor ID if not provided
+ if ( ! $vendor_id ) {
+ $vendor = $this->get_vendor( $request );
+ if ( ! is_wp_error( $vendor ) && $vendor->get_id() ) {
+ $vendor_id = $vendor->get_id();
+ }
+ }
+
$request->set_param( 'id', $vendor_id );
return parent::get_store( $request );
}
🤖 Prompt for AI Agents |
||
|
|
||
| /** | ||
|
|
@@ -176,7 +162,7 @@ | |
| * | ||
| * @return array Links for the given post. | ||
| */ | ||
| protected function prepare_links( $object, $request ) { | ||
| $links = [ | ||
| 'self' => [ | ||
| 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object['id'] ) ), | ||
|
|
@@ -199,7 +185,8 @@ | |
| * @return \WP_REST_Response $response Response data. | ||
| */ | ||
| public function prepare_item_for_response( $store, $request, $additional_fields = [] ) { | ||
| $data = $store->to_array(); | ||
| $response = parent::prepare_item_for_response( $store, $request, $additional_fields ); | ||
| $data = $response->get_data(); | ||
| $data = array_merge( $data, apply_filters( 'dokan_rest_store_settings_additional_fields', $additional_fields, $store, $request ) ); | ||
| $response = rest_ensure_response( $data ); | ||
| $response->add_links( $this->prepare_links( $data, $request ) ); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edge case:
vendor_id = 0when parameter is not provided.When
vendor_idis not in the request,(int) $request->get_param( 'vendor_id' )returns0. This0is then set as theidparameter, which will causeparent::update_store()to fail with a 404 error since store ID 0 is invalid.The
get_vendormethod handles missingvendor_idby falling back to the current user, butupdate_settingsdoesn't use that fallback. Consider aligning the behavior.🔎 Proposed fix
public function update_settings( $request ) { $vendor_id = (int) $request->get_param( 'vendor_id' ); + + // Fall back to current user's vendor ID if not provided + if ( ! $vendor_id ) { + $vendor = $this->get_vendor( $request ); + if ( ! is_wp_error( $vendor ) && $vendor->get_id() ) { + $vendor_id = $vendor->get_id(); + } + } + $request->set_param( 'id', $vendor_id ); $response = parent::update_store( $request );📝 Committable suggestion