Show contributors for docs and articles in free version#210
Show contributors for docs and articles in free version#210haideralibd wants to merge 9 commits into
Conversation
WalkthroughThis update introduces contributor management for documentation posts, including new backend classes, REST API endpoints, and frontend components to display contributor avatars. It adds logic to aggregate and store contributor data, updates plugin metadata, and refines UI elements to show contributor information and track upgrade button clicks. Changes
Suggested labels
Suggested reviewers
Poem
Note ⚡️ AI Code Reviews for VS Code, Cursor, WindsurfCodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback. Note ⚡️ Faster reviews with cachingCodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure ✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
…functions to free plugin
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (10)
src/components/Documentations/ContributorUsers.js (2)
45-65: Simplify conditional rendering with optional chaining.The code can be simplified by using optional chaining more effectively.
- { userAvatars && - userAvatars?.map( ( avatar, index ) => + { userAvatars?.map( ( avatar, index ) => index < 4 ? ( <div key={ index } className="tooltip cursor-pointer" data-tip={ __( avatar?.name, 'wedocs-pro' ) } > <img src={ avatar?.url } alt={ avatar?.name } className="relative z-0 inline-block h-full w-7 rounded-full ring-2 ring-white" /> </div> ) : ( '' ) ) }🧰 Tools
🪛 Biome (1.9.4)
[error] 45-65: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
67-73: Simplify additional contributors display with optional chaining.Similar to the above comment, this can be simplified with better optional chaining.
- { userAvatars && userAvatars?.length > 4 && ( + { userAvatars?.length > 4 && ( <div className="relative z-0 inline-block h-full w-7 rounded-full ring-2 ring-white bg-indigo-700 text-white"> <span className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 -mt-[1px]"> +{ userAvatars?.length - 4 } </span> </div> ) }includes/Hooks.php (1)
33-38: Clarify the post type hierarchy check.The comment says "Check if this is a section post" but the code is actually checking if the post is a third-level document.
- // Check if this is a section post. + // Check if this post is an article (has a section parent) $parent_id = wp_get_post_parent_id( $post->post_parent ); if ( false === $parent_id ) { return; }includes/WedocsContributor.php (3)
17-26: Consider using a more reliable method to initialize contributors.Using an option to track initialization is simple but might cause issues if the option gets accidentally deleted.
public function check_and_add_docs_contributors() { $contributors_added = get_option( 'wedocs_contributors_added', null ); - if ( null === $contributors_added ) { + // Check if we need to initialize contributors + if ( null === $contributors_added || apply_filters( 'wedocs_force_contributors_init', false ) ) { $this->add_article_contributors(); $this->add_documentation_contributors(); update_option( 'wedocs_contributors_added', true ); } }
35-50: Consider adding pagination for better performance with large datasets.Retrieving all posts at once without pagination can lead to memory issues with large datasets.
public function add_article_contributors() { - $args = array( - 'post_type' => 'docs', - 'post_status' => array( 'publish', 'private' ), - 'posts_per_page' => -1, - ); - - $documentations = get_posts( $args ); - - foreach ( $documentations as $documentation ) { - $contributors = get_post_meta( $documentation->ID, 'wedocs_contributors', true ); - if ( empty( $contributors ) && 0 !== $documentation->post_parent ) { - update_post_meta( $documentation->ID, 'wedocs_contributors', array( $documentation->post_author ) ); + $paged = 1; + $batch_size = 100; // Process in batches for better memory management + + do { + $args = array( + 'post_type' => 'docs', + 'post_status' => array( 'publish', 'private' ), + 'posts_per_page' => $batch_size, + 'paged' => $paged, + ); + + $documentations = get_posts( $args ); + + foreach ( $documentations as $documentation ) { + $contributors = get_post_meta( $documentation->ID, 'wedocs_contributors', true ); + if ( empty( $contributors ) && 0 !== $documentation->post_parent ) { + update_post_meta( $documentation->ID, 'wedocs_contributors', array( $documentation->post_author ) ); + } } - } + + $paged++; + } while ( count( $documentations ) === $batch_size ); }
59-77: Apply similar pagination improvements for parent documentation processing.Similar to the article contributors method, this one also retrieves all posts at once which could be optimized.
This method would benefit from the same batch processing approach recommended for the
add_article_contributorsmethod to avoid memory issues with large numbers of documents.includes/functions.php (4)
583-604: Good implementation of contributor trackingThe function effectively updates the documentation's contributor list by identifying new contributors from articles and merging them with existing ones.
Consider adding validation to check if the article ID exists and is of type 'docs'. Also, you might want to handle the case where an article doesn't have a grandparent (when
$ancestorsis empty):function wedocs_plugin_update_documentation_contributors( $article_id, $article_contributors ) { + if ( empty( $article_id ) || get_post_type( $article_id ) !== 'docs' ) { + return; + } $ancestors = get_post_ancestors( $article_id ); + if ( empty( $ancestors ) ) { + return; + } $grand_parent_id = end( $ancestors ); // Rest of the function... }
606-645: Well-structured function with appropriate validationThe function properly validates inputs and handles different child types correctly. The separation of section and article retrieval logic is clean.
The PHPDoc could be more specific about the accepted values for
$children_type:/** * Get documentation children ids by using children type. * * @since 2.1.12 * * @param int $doc_id Parent documentation id. * @param string $children_type Children type. + * Accepts 'article' or 'section' only. * @param array $custom_args Optional. Additional query arguments. * * @return array Children (article/section) ids array */
647-669: Efficiently aggregates contributors across documentationThe function correctly collects unique contributors from all related articles and includes the doc creator.
Consider validating the
$doc_idparameter and using array casting more consistently:function wedocs_plugin_get_documentation_contributors( $doc_id, $article_ids ) { + if ( empty( $doc_id ) || get_post_type( $doc_id ) !== 'docs' ) { + return array(); + } $contributors = array( $doc_id ); + $article_ids = (array) $article_ids; foreach ( $article_ids as $article_id ) { $article_contributors = get_post_meta( $article_id, 'wedocs_contributors', true ); - foreach ( (array) $article_contributors as $contributor ) { + $article_contributors = (array) $article_contributors; + foreach ( $article_contributors as $contributor ) { if ( ! empty( $contributor ) && ! in_array( $contributor, $contributors ) ) { array_push( $contributors, absint( $contributor ) ); } } } return $contributors; }
671-671: Remove unnecessary empty lineThere's an extra empty line at line 671 that could be removed for consistency.
- /** Convert UTC Time zone to EST timezone
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
includes/API/API.php(2 hunks)includes/Hooks.php(1 hunks)includes/WedocsContributor.php(1 hunks)includes/functions.php(1 hunks)package.json(1 hunks)readme.txt(1 hunks)src/components/Documentations/ContributorUsers.js(1 hunks)src/components/ProPreviews/common/UpgradeButton.js(1 hunks)src/components/ProPreviews/index.js(2 hunks)wedocs.php(6 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/components/ProPreviews/index.js (1)
src/components/Documentations/ContributorUsers.js (1)
ContributorUsers(5-76)
includes/WedocsContributor.php (2)
wedocs.php (1)
WeDocs(58-316)includes/functions.php (2)
wedocs_plugin_get_documentation_children_by_type(616-645)wedocs_plugin_get_documentation_contributors(657-669)
🪛 Biome (1.9.4)
src/components/Documentations/ContributorUsers.js
[error] 45-65: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
🔇 Additional comments (13)
readme.txt (1)
8-8: Update Stable Tag for ReleaseBumped
Stable tagto2.1.12to align with the new contributor management features in this release. Please verify that the main plugin file header is also updated to version2.1.12.package.json (1)
3-3: Version number correctly updated.The version bump from 2.1.11 to 2.1.12 is consistent with the feature additions for contributor management in this PR.
src/components/ProPreviews/common/UpgradeButton.js (1)
11-11: Improved upgrade URL with tracking parameters.The upgrade button URL now includes UTM parameters, which will help track the effectiveness of upgrade prompts from the WordPress dashboard.
wedocs.php (5)
6-6: Version number correctly updated.The version has been updated to 2.1.12 in both the plugin header and the VERSION constant, maintaining consistency with package.json.
Also applies to: 65-65
43-44: New classes imported for contributor functionality.The imports for Hooks and WedocsContributor classes support the new contributor management feature.
117-124: New method added for managing contributor data.The new method correctly initializes the WedocsContributor class and calls its contributor checking method.
185-185: Appropriate hook timing for contributor processing.Using 'plugins_loaded' as the hook for checking contributors ensures all plugins are loaded before processing begins.
241-241: Hooks class properly initialized.The Hooks class is correctly instantiated and stored in the plugin container.
src/components/ProPreviews/index.js (3)
4-4: Component import updated to new ContributorUsers component.The import has been updated to use the new contributor display component from the Documentations directory.
226-226: Fixed syntax error in conditional block.Proper closing brace added to fix the block structure of the
if (!isProLoaded)condition.
228-246: Contributor display hooks modified to work with new component.The hooks now:
- Accept document ID as a parameter
- Pass this ID to the ContributorUsers component
- Maintain the admin-only access check
- Are positioned outside the Pro feature condition, enabling contributor display in the free version
This implementation aligns with the PR objective to show contributors for docs and articles in the free version.
includes/API/API.php (1)
237-243: Clean API endpoint registration for specific document contributors.The endpoint is properly registered with the correct namespace and path pattern, ensuring it follows the same structure as other endpoints in the file.
includes/Hooks.php (1)
12-14: Properly implemented hook registration.The hook is registered correctly to capture documentation post saves.
| /** | ||
| * Check permissions for getting | ||
| * Get specific documentation contributors. | ||
| * | ||
| * @since 2.1.12 | ||
| * | ||
| * @param \WP_REST_Request $request | ||
| * | ||
| * @return mixed | ||
| */ | ||
| public function get_specific_documentation_contributors( $request ) { | ||
| $id = absint( $request->get_param( 'id' ) ); | ||
| $key = 'wedocs_contributors'; | ||
|
|
||
| $contributor_ids = get_post_meta( $id, $key, true ); | ||
|
|
||
| return rest_ensure_response( $contributor_ids ); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Add validation and proper error handling to the contributor endpoint.
While the implementation is straightforward, it lacks proper validation and error handling.
Consider implementing these improvements:
public function get_specific_documentation_contributors( $request ) {
$id = absint( $request->get_param( 'id' ) );
$key = 'wedocs_contributors';
+
+ // Check if document exists and is valid
+ $post = get_post( $id );
+ if ( ! $post || 'docs' !== $post->post_type ) {
+ return new WP_Error(
+ 'wedocs_invalid_doc',
+ __( 'Invalid documentation ID.', 'wedocs' ),
+ array( 'status' => 404 )
+ );
+ }
$contributor_ids = get_post_meta( $id, $key, true );
+ $contributor_ids = ! empty( $contributor_ids ) ? (array) $contributor_ids : array();
return rest_ensure_response( $contributor_ids );
}🤖 Prompt for AI Agents
In includes/API/API.php around lines 863 to 879, the
get_specific_documentation_contributors method lacks validation and error
handling for the input and data retrieval. Add validation to ensure the 'id'
parameter is valid and corresponds to an existing post. If the 'id' is invalid
or no contributors are found, return an appropriate error response using
WP_Error with a relevant status code. This will improve robustness and provide
clear feedback to API consumers.
| useEffect( () => { | ||
| apiFetch( { | ||
| path: '/wp/v2/docs/' + id + '/contributors', | ||
| } ) | ||
| .then( ( result ) => { | ||
| setContributors( [ ...contributors, ...result ] ); | ||
| } ) | ||
| .catch( ( err ) => { | ||
| console.log( err ); | ||
| } ); | ||
| }, [ id ] ); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Fix spread logic in the contributors state update.
The current implementation could lead to duplicate contributors if the effect runs multiple times.
useEffect( () => {
apiFetch( {
path: '/wp/v2/docs/' + id + '/contributors',
} )
.then( ( result ) => {
- setContributors( [ ...contributors, ...result ] );
+ setContributors( result || [] );
} )
.catch( ( err ) => {
console.log( err );
} );
}, [ id ] );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect( () => { | |
| apiFetch( { | |
| path: '/wp/v2/docs/' + id + '/contributors', | |
| } ) | |
| .then( ( result ) => { | |
| setContributors( [ ...contributors, ...result ] ); | |
| } ) | |
| .catch( ( err ) => { | |
| console.log( err ); | |
| } ); | |
| }, [ id ] ); | |
| useEffect( () => { | |
| apiFetch( { | |
| path: '/wp/v2/docs/' + id + '/contributors', | |
| } ) | |
| .then( ( result ) => { | |
| setContributors( result || [] ); | |
| } ) | |
| .catch( ( err ) => { | |
| console.log( err ); | |
| } ); | |
| }, [ id ] ); |
🤖 Prompt for AI Agents
In src/components/Documentations/ContributorUsers.js between lines 9 and 19, the
useEffect hook updates the contributors state by spreading the existing
contributors and the new result, which can cause duplicates if the effect runs
multiple times. To fix this, update the state by merging the existing
contributors and new results while removing duplicates, for example by using a
Set or filtering logic, ensuring each contributor appears only once in the
state.
| useEffect( () => { | ||
| contributors?.map( ( contributorId ) => { | ||
| apiFetch( { | ||
| path: '/wp/v2/users/' + contributorId, | ||
| } ) | ||
| .then( ( result ) => { | ||
| const userName = result?.name, | ||
| avatarUrl = result?.avatar_urls?.[ '24' ]; | ||
|
|
||
| userAvatars.push( { | ||
| name: userName, | ||
| url: avatarUrl, | ||
| } ); | ||
|
|
||
| setUserAvatars( [ ...userAvatars ] ); | ||
| } ) | ||
| .catch( ( err ) => { | ||
| console.log( err ); | ||
| } ); | ||
| } ); | ||
| }, [ contributors ] ); |
There was a problem hiding this comment.
Improve user data fetching logic to avoid state mutation issues.
The current implementation has several issues:
- Directly mutates state with
push() - Uses
map()without returning anything - Could trigger multiple unnecessary re-renders
useEffect( () => {
- contributors?.map( ( contributorId ) => {
+ if (!contributors || !contributors.length) return;
+
+ const fetchUserData = async () => {
+ try {
+ const avatarData = await Promise.all(contributors.map(async (contributorId) => {
+ const result = await apiFetch({ path: '/wp/v2/users/' + contributorId });
+ return {
+ name: result?.name,
+ url: result?.avatar_urls?.['24']
+ };
+ }));
+ setUserAvatars(avatarData);
+ } catch (err) {
+ console.error('Error fetching user data:', err);
+ }
+ };
+
+ fetchUserData();
- apiFetch( {
- path: '/wp/v2/users/' + contributorId,
- } )
- .then( ( result ) => {
- const userName = result?.name,
- avatarUrl = result?.avatar_urls?.[ '24' ];
-
- userAvatars.push( {
- name: userName,
- url: avatarUrl,
- } );
-
- setUserAvatars( [ ...userAvatars ] );
- } )
- .catch( ( err ) => {
- console.log( err );
- } );
- } );
}, [ contributors ] );📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| useEffect( () => { | |
| contributors?.map( ( contributorId ) => { | |
| apiFetch( { | |
| path: '/wp/v2/users/' + contributorId, | |
| } ) | |
| .then( ( result ) => { | |
| const userName = result?.name, | |
| avatarUrl = result?.avatar_urls?.[ '24' ]; | |
| userAvatars.push( { | |
| name: userName, | |
| url: avatarUrl, | |
| } ); | |
| setUserAvatars( [ ...userAvatars ] ); | |
| } ) | |
| .catch( ( err ) => { | |
| console.log( err ); | |
| } ); | |
| } ); | |
| }, [ contributors ] ); | |
| useEffect( () => { | |
| if ( !contributors || !contributors.length ) { | |
| return; | |
| } | |
| const fetchUserData = async () => { | |
| try { | |
| const avatarData = await Promise.all( | |
| contributors.map( async ( contributorId ) => { | |
| const result = await apiFetch( { | |
| path: '/wp/v2/users/' + contributorId, | |
| } ); | |
| return { | |
| name: result?.name, | |
| url: result?.avatar_urls?.[ '24' ], | |
| }; | |
| } ) | |
| ); | |
| setUserAvatars( avatarData ); | |
| } catch ( err ) { | |
| console.error( 'Error fetching user data:', err ); | |
| } | |
| }; | |
| fetchUserData(); | |
| }, [ contributors ] ); |
🤖 Prompt for AI Agents
In src/components/Documentations/ContributorUsers.js around lines 21 to 41, the
useEffect hook directly mutates the userAvatars state array using push(), uses
map() without returning values, and causes multiple re-renders by calling
setUserAvatars inside each fetch. To fix this, avoid mutating state directly by
creating a new array, use Promise.all to fetch all user data concurrently,
collect the results into a new array, and then update the state once with the
complete array to minimize re-renders.
| public function save_documentation_contributors( $post_id, $post ) { | ||
| // Check if this is a parent post. | ||
| if ( 0 === $post->post_parent ) { | ||
| $this->save_parent_contributors( $post ); | ||
| return; | ||
| } | ||
|
|
||
| // Check if this is a section post. | ||
| $parent_id = wp_get_post_parent_id( $post->post_parent ); | ||
|
|
||
| if ( false === $parent_id ) { | ||
| return; | ||
| } | ||
|
|
||
| $user_id = get_current_user_id(); | ||
| $contributors = get_post_meta( $post_id, 'wedocs_contributors', true ); | ||
| $contributors = ! empty( $contributors ) ? $contributors : array(); | ||
|
|
||
| if ( ! in_array( $user_id, $contributors ) ) { | ||
| array_push( $contributors, $user_id ); | ||
| } | ||
|
|
||
| // Save the doc contributors meta. | ||
| update_post_meta( $post_id, 'wedocs_contributors', (array) $contributors ); | ||
| wedocs_plugin_update_documentation_contributors( $post_id, $contributors ); | ||
| } |
There was a problem hiding this comment.
Add proper nonce verification and capability checks.
The current implementation doesn't verify if the post save is legitimate and doesn't check if the user has proper capabilities.
public function save_documentation_contributors( $post_id, $post ) {
+ // Don't save on autosave
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
+ return;
+ }
+
+ // Check if user has permission to edit this post
+ if ( ! current_user_can( 'edit_post', $post_id ) ) {
+ return;
+ }
+
// Check if this is a parent post.
if ( 0 === $post->post_parent ) {
$this->save_parent_contributors( $post );
return;
}
// Check if this is a section post.
$parent_id = wp_get_post_parent_id( $post->post_parent );Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In includes/Hooks.php around lines 26 to 51, the save_documentation_contributors
function lacks nonce verification and user capability checks, which are
necessary to ensure the post save action is legitimate and authorized. Add a
nonce verification step to validate the request's authenticity and include a
capability check to confirm the current user has permission to edit the post
before proceeding with saving contributors. This will secure the function
against unauthorized or malicious requests.
| public function save_parent_contributors( $post ) { | ||
| $article_ids = wedocs_plugin_get_documentation_children_by_type( $post->ID ); | ||
| $contributors = wedocs_plugin_get_documentation_contributors( $post->post_author, $article_ids ); | ||
|
|
||
| update_post_meta( $post->ID, 'wedocs_contributors', (array) $contributors ); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Ensure contributors data is properly handled.
The current implementation assumes the helper functions will return valid data without any error checking.
public function save_parent_contributors( $post ) {
$article_ids = wedocs_plugin_get_documentation_children_by_type( $post->ID );
$contributors = wedocs_plugin_get_documentation_contributors( $post->post_author, $article_ids );
+ // Ensure we have valid contributor data
+ $contributors = is_array( $contributors ) ? $contributors : array( $post->post_author );
update_post_meta( $post->ID, 'wedocs_contributors', (array) $contributors );
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public function save_parent_contributors( $post ) { | |
| $article_ids = wedocs_plugin_get_documentation_children_by_type( $post->ID ); | |
| $contributors = wedocs_plugin_get_documentation_contributors( $post->post_author, $article_ids ); | |
| update_post_meta( $post->ID, 'wedocs_contributors', (array) $contributors ); | |
| } | |
| public function save_parent_contributors( $post ) { | |
| $article_ids = wedocs_plugin_get_documentation_children_by_type( $post->ID ); | |
| $contributors = wedocs_plugin_get_documentation_contributors( $post->post_author, $article_ids ); | |
| // Ensure we have valid contributor data | |
| $contributors = is_array( $contributors ) ? $contributors : array( $post->post_author ); | |
| update_post_meta( $post->ID, 'wedocs_contributors', (array) $contributors ); | |
| } |
🤖 Prompt for AI Agents
In includes/Hooks.php around lines 62 to 67, the save_parent_contributors method
lacks error handling for the helper functions
wedocs_plugin_get_documentation_children_by_type and
wedocs_plugin_get_documentation_contributors. Add checks to verify that these
functions return valid, non-empty data before proceeding. If either returns
null, false, or an unexpected value, handle it gracefully by setting
contributors to an empty array or logging an error, ensuring update_post_meta is
called with valid data only.
Summary by CodeRabbit