Skip to content
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

Adds ability to manage plugins for self hosted sites that aren't connected to Jetpack #16675

Merged
merged 33 commits into from
Jun 18, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
991e257
Add support for self hosted sites in the supportsPluginManagement logic
Jun 11, 2021
3644eab
Add a way support self hosted non Jetpack sites in the JetpackSiteRef
Jun 11, 2021
261609e
Fix supportsPluginManagement logic to prevent a crash
Jun 11, 2021
40034dc
Add a helper method to get a blog using the XMLRPC
Jun 11, 2021
3ebdf9c
Add helper method to exclude siteID from self hosted plugin events
Jun 11, 2021
f20b3c3
Add a protocol to represent a generic plugin management client interface
Jun 11, 2021
d964582
Add PluginServiceRemove methods to a new class that adheres to Plugin…
Jun 11, 2021
a567e9a
Add a self hosted plugin management client
Jun 11, 2021
cebc904
Use the PluginManagementClient instead of the PluginServiceRemote
Jun 11, 2021
cbbabaf
Update PodFile to point to WordPressKit branch
Jun 11, 2021
6217ae2
Remove unneeded enum value
Jun 11, 2021
b593845
Fix ending up in a limbo state when installing
Jun 11, 2021
586bf77
Update RELEASE-NOTES.txt
Jun 11, 2021
bbc354c
Merge branch 'develop' into task/16595-self-hosted-plugins
Jun 11, 2021
9f4c7ff
Update Podfile.lock
Jun 11, 2021
2777a5d
Update RELEASE-NOTES.txt
Jun 11, 2021
2289915
Merge branch 'develop' into task/16595-self-hosted-plugins
Jun 14, 2021
9ada0ef
Fix Podfile.lock updating the wrong pods
Jun 14, 2021
7882ac9
Update BlogBuilder to support more testing options
Jun 14, 2021
b89d3bf
Add Plugin Management Tests
Jun 14, 2021
a34ded9
Update JetpackRef.mock to fix broken tests
Jun 14, 2021
7df619d
Add nil account check to the supportsPluginManagement method
Jun 14, 2021
6f2852d
Add decouple the use of JetpackSiteRef from the PluginManagementClient
Jun 15, 2021
16e0361
Remove Plugin Management code that's been moved to WPKit
Jun 15, 2021
18ef501
Update WPKit commit
Jun 15, 2021
28bf51e
Update SelfHostedPluginManagementClient init
Jun 15, 2021
5931d10
Allow lookup of a plugin based on its slugs prefix
Jun 15, 2021
bf63b89
Use the found plugins ID for network actions
Jun 15, 2021
3c163b8
Add support for getting the PluginDirectoryEntry for self hosted plugins
Jun 15, 2021
426ad9c
Update release notes to ignore the self hosted feature notes from the…
Jun 18, 2021
79c9e73
Point to WPKit 4.36.0-beta.2
Jun 18, 2021
aedc0b5
Merge branch 'develop' into task/16595-self-hosted-plugins
Jun 18, 2021
2fcfc27
Update Podfile.lock to WPKit 4.36.0-beta.2
Jun 18, 2021
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
4 changes: 2 additions & 2 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def wordpress_ui
end

def wordpress_kit
pod 'WordPressKit', '~> 4.35.0-beta.1'
#pod 'WordPressKit', '~> 4.35.0-beta.1'
# pod 'WordPressKit', :git => 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', :tag => ''
# pod 'WordPressKit', :git => 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', :branch => 'fix/response-code-for-org-failed-login'
pod 'WordPressKit', :git => 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', :branch => 'task/16595-self-hosted-plugins'
# pod 'WordPressKit', :git => 'https://github.com/wordpress-mobile/WordPressKit-iOS.git', :commit => ''
# pod 'WordPressKit', :path => '../WordPressKit-iOS'
end
Expand Down
15 changes: 10 additions & 5 deletions Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ PODS:
- WordPressKit (~> 4.18-beta)
- WordPressShared (~> 1.12-beta)
- WordPressUI (~> 1.7-beta)
- WordPressKit (4.35.0-beta.1):
- WordPressKit (4.35.0-beta.2):
- Alamofire (~> 4.8.0)
- CocoaLumberjack (~> 3.4)
- NSObject-SafeExpectations (= 0.0.4)
Expand Down Expand Up @@ -499,7 +499,7 @@ DEPENDENCIES:
- SVProgressHUD (= 2.2.5)
- WordPress-Editor-iOS (~> 1.19.4)
- WordPressAuthenticator (~> 1.38.0-beta)
- WordPressKit (~> 4.35.0-beta.1)
- WordPressKit (from `https://github.com/wordpress-mobile/WordPressKit-iOS.git`, branch `task/16595-self-hosted-plugins`)
- WordPressMocks (~> 0.0.12)
- WordPressShared (~> 1.16.0)
- WordPressUI (~> 1.12.1-beta)
Expand Down Expand Up @@ -551,7 +551,6 @@ SPEC REPOS:
- UIDeviceIdentifier
- WordPress-Aztec-iOS
- WordPress-Editor-iOS
- WordPressKit
- WordPressMocks
- WordPressShared
- WPMediaPicker
Expand Down Expand Up @@ -651,6 +650,9 @@ EXTERNAL SOURCES:
:git: https://github.com/wordpress-mobile/gutenberg-mobile.git
:submodules: true
:tag: v1.55.0
WordPressKit:
:branch: task/16595-self-hosted-plugins
:git: https://github.com/wordpress-mobile/WordPressKit-iOS.git
Yoga:
:podspec: https://raw.githubusercontent.com/wordpress-mobile/gutenberg-mobile/v1.55.0/third-party-podspecs/Yoga.podspec.json

Expand All @@ -666,6 +668,9 @@ CHECKOUT OPTIONS:
:git: https://github.com/wordpress-mobile/gutenberg-mobile.git
:submodules: true
:tag: v1.55.0
WordPressKit:
:commit: 97ac7934272260dc2b70a5291365a0b4ae4bfff4
:git: https://github.com/wordpress-mobile/WordPressKit-iOS.git

SPEC CHECKSUMS:
1PasswordExtension: f97cc80ae58053c331b2b6dc8843ba7103b33794
Expand Down Expand Up @@ -747,7 +752,7 @@ SPEC CHECKSUMS:
WordPress-Aztec-iOS: 870c93297849072aadfc2223e284094e73023e82
WordPress-Editor-iOS: 068b32d02870464ff3cb9e3172e74234e13ed88c
WordPressAuthenticator: 6031e2aa349113aa6a36a8ce489fb2c75d273e00
WordPressKit: 5894cd7385898bc7dfaf75073bcce0ffdd1a1807
WordPressKit: 86804222637c3e5d7f2b897d7a55741f6917372c
WordPressMocks: 2783c51aaa34eca64d6ebbad13837951c374baf2
WordPressShared: 0f7f10e96f8354d64f951c223ae61e8de7495a46
WordPressUI: 3696cfef6f0ca595fb19df64f5bee6d2b3021e7e
Expand All @@ -763,6 +768,6 @@ SPEC CHECKSUMS:
ZendeskSupportSDK: e100a7a0a1bb5d7d43abbde3338727d985a4986d
ZIPFoundation: e27423c004a5a1410c15933407747374e7c6cb6e

PODFILE CHECKSUM: f7f4f2c65e783b0179359225cf9232394157b8ae
PODFILE CHECKSUM: c7632605b59234b9f5794883f68793535452d2d5

COCOAPODS: 1.10.1
4 changes: 4 additions & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
17.7
-----
* [**] Self hosted sites that do not use Jetpack can now manage (install, modify active, uninstall) their plugins [#16675]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please add (Don't apply to Jetpack app) before the change description?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done! 426ad9c


17.6
-----
* [**] Reader Post details: now shows a summary of Likes for the post. Tapping it displays the full list of Likes. [#16628]
Expand Down
24 changes: 21 additions & 3 deletions WordPress/Classes/Models/Blog.m
Original file line number Diff line number Diff line change
Expand Up @@ -628,11 +628,22 @@ - (BOOL)supportsPluginManagement
BOOL hasRequiredJetpack = [self hasRequiredJetpackVersion:@"5.6"];

BOOL isTransferrable = self.isHostedAtWPcom
&& self.hasBusinessPlan
&& self.siteVisibility != SiteVisibilityPrivate
&& self.hasBusinessPlan
&& self.siteVisibility != SiteVisibilityPrivate
&& self.isAdmin;

BOOL supports = isTransferrable || hasRequiredJetpack;

// If the site is not hosted on WP.com we can still manage plugins directly using the WP.org rest API
// Reference: https://make.wordpress.org/core/2020/07/16/new-and-modified-rest-api-endpoints-in-wordpress-5-5/
if(!supports && !self.account){
supports = !self.isHostedAtWPcom
&& self.wordPressOrgRestApi
&& [self hasRequiredWordPressVersion:@"5.5"]
&& self.isAdmin;
}

return isTransferrable || hasRequiredJetpack;
return supports;
}

- (BOOL)supportsStories
Expand Down Expand Up @@ -853,6 +864,13 @@ - (BOOL)hasRequiredJetpackVersion:(NSString *)requiredJetpackVersion
&& [self.jetpack.version compare:requiredJetpackVersion options:NSNumericSearch] != NSOrderedAscending;
}

/// Checks the blogs installed WordPress version is more than or equal to the requiredVersion
/// @param requiredVersion The minimum version to check for
- (BOOL)hasRequiredWordPressVersion:(NSString *)requiredVersion
{
return [self.version compare:requiredVersion options:NSNumericSearch] != NSOrderedAscending;
leandroalonso marked this conversation as resolved.
Show resolved Hide resolved
}

#pragma mark - Private Methods

- (id)getOptionValue:(NSString *)name
Expand Down
55 changes: 46 additions & 9 deletions WordPress/Classes/Models/JetpackSiteRef.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,53 @@ struct JetpackSiteRef: Hashable, Codable {
let homeURL: String

private var hasBackup = false

private var hasPaidPlan = false

// Self Hosted Non Jetpack Support
// Ideally this would be a different "ref" object but the JetpackSiteRef
// is so coupled into the plugin management that the amount of changes and work needed to change
// would be very large. This is a workaround for that.
let isSelfHostedWithoutJetpack: Bool
leandroalonso marked this conversation as resolved.
Show resolved Hide resolved

/// The XMLRPC path for the site, only applies to self hosted sites with no Jetpack connected
var xmlRPC: String? = nil

init?(blog: Blog) {
guard let username = blog.account?.username,
let siteID = blog.dotComID as? Int,
let homeURL = blog.homeURL as String? else {

// Init for self hosted and no Jetpack
if blog.account == nil, !blog.isHostedAtWPcom {
guard
let username = blog.username,
let homeURL = blog.homeURL as String?,
let xmlRPC = blog.xmlrpc
else {
return nil
}

self.isSelfHostedWithoutJetpack = true
self.username = username
self.siteID = Constants.selfHostedSiteID
self.homeURL = homeURL
self.xmlRPC = xmlRPC
}

// Init for normal Jetpack connected sites
else {
guard
let username = blog.account?.username,
let siteID = blog.dotComID as? Int,
let homeURL = blog.homeURL as String?
else {
return nil
}

self.isSelfHostedWithoutJetpack = false
self.siteID = siteID
self.username = username
self.homeURL = homeURL
self.hasBackup = blog.isBackupsAllowed()
self.hasPaidPlan = blog.hasPaidPlan
}
self.siteID = siteID
self.username = username
self.homeURL = homeURL
self.hasBackup = blog.isBackupsAllowed()
self.hasPaidPlan = blog.hasPaidPlan
}

public func hash(into hasher: inout Hasher) {
Expand All @@ -48,4 +81,8 @@ struct JetpackSiteRef: Hashable, Codable {
func shouldShowActivityLogFilter() -> Bool {
hasBackup || hasPaidPlan
}

struct Constants {
static let selfHostedSiteID = -1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Foundation
import WordPressKit

class JetpackPluginManagementClient: PluginManagementClient {
emilylaguna marked this conversation as resolved.
Show resolved Hide resolved
private let siteID: Int
private let remote: PluginServiceRemote

required init?(with site: JetpackSiteRef) {
leandroalonso marked this conversation as resolved.
Show resolved Hide resolved
guard let token = CredentialsService().getOAuthToken(site: site) else {
return nil
}

siteID = site.siteID

let api = WordPressComRestApi.defaultApi(oAuthToken: token, userAgent: WPUserAgent.wordPress())
remote = PluginServiceRemote(wordPressComRestApi: api)
}

func getPlugins(success: @escaping (SitePlugins) -> Void, failure: @escaping (Error) -> Void) {
remote.getPlugins(siteID: siteID, success: success, failure: failure)
}

func updatePlugin(pluginID: String, success: @escaping (PluginState) -> Void, failure: @escaping (Error) -> Void) {
remote.updatePlugin(pluginID: pluginID, siteID: siteID, success: success, failure: failure)
}

func activatePlugin(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
remote.activatePlugin(pluginID: pluginID, siteID: siteID, success: success, failure: failure)
}

func deactivatePlugin(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
remote.deactivatePlugin(pluginID: pluginID, siteID: siteID, success: success, failure: failure)
}

func enableAutoupdates(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
remote.enableAutoupdates(pluginID: pluginID, siteID: siteID, success: success, failure: failure)

}

func disableAutoupdates(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
remote.disableAutoupdates(pluginID: pluginID, siteID: siteID, success: success, failure: failure)
}

func activateAndEnableAutoupdates(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
remote.activateAndEnableAutoupdates(pluginID: pluginID, siteID: siteID, success: success, failure: failure)
}

func install(pluginSlug: String, success: @escaping (PluginState) -> Void, failure: @escaping (Error) -> Void) {
remote.install(pluginSlug: pluginSlug, siteID: siteID, success: success, failure: failure)
}

func remove(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void) {
remote.remove(pluginID: pluginID, siteID: siteID, success: success, failure: failure)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

protocol PluginManagementClient {
emilylaguna marked this conversation as resolved.
Show resolved Hide resolved
init?(with site: JetpackSiteRef)
emilylaguna marked this conversation as resolved.
Show resolved Hide resolved

func getPlugins(success: @escaping (SitePlugins) -> Void, failure: @escaping (Error) -> Void)
func updatePlugin(pluginID: String, success: @escaping (PluginState) -> Void, failure: @escaping (Error) -> Void)
func activatePlugin(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func deactivatePlugin(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func enableAutoupdates(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func disableAutoupdates(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func activateAndEnableAutoupdates(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
func install(pluginSlug: String, success: @escaping (PluginState) -> Void, failure: @escaping (Error) -> Void)
func remove(pluginID: String, success: @escaping () -> Void, failure: @escaping (Error) -> Void)
}
Loading