Skip to content

REST API: JPO - Install WooCommerce#8450

Closed
ockham wants to merge 4 commits intomasterfrom
add/jpo-install-woocommerce
Closed

REST API: JPO - Install WooCommerce#8450
ockham wants to merge 4 commits intomasterfrom
add/jpo-install-woocommerce

Conversation

@ockham
Copy link
Copy Markdown
Contributor

@ockham ockham commented Jan 3, 2018

Counterpart to Automattic/wp-calypso#21237.

Code mostly stolen from https://github.com/Automattic/jetpack-onboarding/blob/06c6c51417b95d7b766ee5aad762769b9c208571/class.jetpack-onboarding-end-points.php#L582-L615. I tried to find prior art for plugin installation in Jetpack itself, but only found a dedicated endpoint. If we were to use code from there, we'd have to move it to a shared file; not sure that makes sense, since the install endpoint code also included bulk install logic which we don't need.

I've also noted that @tyxla left some comments on the PR that originally added WooCommerce installation to the JPO plugin, but I don't know the mentioned core functions well enough to make sense of it 😳.

@ockham ockham self-assigned this Jan 3, 2018
@ockham ockham requested review from AnnaMag and tyxla January 3, 2018 18:03
@ockham ockham requested a review from a team as a code owner January 3, 2018 18:03
@ockham ockham added [Status] Needs Review This PR is ready for review. and removed [Status] In Progress labels Jan 4, 2018
Copy link
Copy Markdown
Member

@tyxla tyxla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work! Tests beautifully in all cases (no WC, WC installed but inactive, WC installed and active)! 👍 💯 🎯

Left several comments, nothing major really.

}
}

if ( isset( $data['installWooCommerce'] ) && $data['installWooCommerce'] ) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isset( $data['installWooCommerce'] ) && $data['installWooCommerce']

can be simplified to:

! empty( $data['installWooCommerce'] )

*
* @return bool True if installation succeeded, error object otherwise.
*/
static function _install_plugin( $slug ) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Several things here:

  • this duplicates a lot of what Jetpack_JSON_API_Plugins_Install_Endpoint::install() does 😞 it would be really nice if we can abstract the installation functionality and reuse it in the plugins install endpoint and here as well.
  • why the underscore in the method name?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why the underscore in the method name?

I was mimicking the (private) _process_onboarding() method's nomenclature here (since _install_plugin, while not private, is only used by that private method). I'm perfectly fine with dropping it (especially if I move it to a different file and it becomes a public function).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense. Anyway, I still don't see why we should be prefixing with an underscore - why not just making the method private or protected? That applies to _process_onboarding() too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the idea is just to signal "this method is private" as part of its name (so its immediately visible at call sites). A bit like including type information in a variable name (can't think of anything really ugly now, maybe something like counter_int or __super_ugly_struct_t or CFooBarSubClass 😛).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't follow why prefixing with an underscore would be more immediately visible than having the method defined with protected or private visibility. After all, visibility is specified before the method name. Not to mention that if a method should be considered private, it is beneficial that it should be implemented as such, so it's not inadvertently used as a public one.

*
* @param string $slug Plugin slug.
*
* @return bool True if installation succeeded, error object otherwise.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be bool|WP_Error instead.

include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
include_once ABSPATH . 'wp-admin/includes/file.php';
$upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
$zip_url = "https://downloads.wordpress.org/plugin/{$slug}.latest-stable.zip";
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This essentially duplicates Jetpack_JSON_API_Plugins_Install_Endpoint::generate_wordpress_org_plugin_download_link() 😞


}

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we were to use code from there, we'd have to move it to a shared file; not sure that makes sense, since the install endpoint code also included bulk install logic which we don't need.

I this it makes complete sense to use a shared file; the fact that it was done like that in the install endpoint doesn't make it the best solution IMHO, especially in cases like this, where we're forced to duplicate code because of this decision.

I would assume that any plugin installation/activation/deactivation helper methods would not be defined in the endpoint itself. Rationale: see how the same is done in Jetpack_JSON_API_Plugins_Install_Endpoint, and that prevents us from reusing this functionality.

I'd prefer moving these methods in their own library class, just like Jetpack_Widgets, and load that library with jetpack_require_lib() when we need it. We could eventually migrate the endpoint to use these helpers afterwards.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Started de-duping PR #8464, to be merged before this one.

require_once ABSPATH . 'wp-admin/includes/plugin.php';
}

$WOOCOMMERCE_ID = 'woocommerce/woocommerce.php';
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming nit: if we have those defined as variables, let's lowercase them in order to conform to the coding standards.

These could make a good use of class constants if we move those methods to a separate library class - in that case we could keep the uppercase names.

if ( is_wp_error( $installed ) ) {
return $installed;
}
return activate_plugin( $WOOCOMMERCE_ID );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activate_plugin will return null on successful activation (yeah, it's weird, I agree), we need to account for that and return true in that case.

}
return activate_plugin( $WOOCOMMERCE_ID );
} else if ( ! is_plugin_active( $WOOCOMMERCE_ID ) ) {
return activate_plugin( $WOOCOMMERCE_ID );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activate_plugin will return null on successful activation (yeah, it's weird, I agree), we need to account for that and return true in that case.

Perhaps we can alter both return activate_plugin cases to $activated = activate_plugin( $WOOCOMMERCE_ID );, and then have a single check that relies on ! is_wp_error( $activated ) before actually returning true at the end of the method.

* @return bool True if installation succeeded, error object otherwise.
*/
static function _install_plugin( $slug ) {
if ( is_multisite() && ! current_user_can( 'manage_network' ) ) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a good idea to have a ! current_user_can( 'install_plugins' ) permission check too (for both multisites and single sites)?

if ( is_wp_error( $installed ) ) {
return $installed;
}
return activate_plugin( $WOOCOMMERCE_ID );
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

activate_plugin does not check for any capabilities, so before calling it, we should check whether the user can actually activate plugins (current_user_can( 'activate_plugins' )).

@ockham
Copy link
Copy Markdown
Contributor Author

ockham commented Jan 8, 2018

Closing in favor of the less redundant #8482.

@ockham ockham closed this Jan 8, 2018
@tyxla tyxla deleted the add/jpo-install-woocommerce branch January 8, 2018 11:39
@tyxla tyxla removed the [Status] Needs Review This PR is ready for review. label Jan 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants