@@ -1619,16 +1619,15 @@ open class SiteStore @Inject constructor(
16191619 // updateSite's full-row write skips WP_API_REST_URL and the credential columns, and this fresh
16201620 // site has no local id (it's matched by URL), so persist them via the URL-keyed writers.
16211621 if (! siteModel.isError) {
1622- val username = siteModel.apiRestUsernamePlain
1623- val password = siteModel.apiRestPasswordPlain
1624- if (! username.isNullOrEmpty() && ! password.isNullOrEmpty()) {
1625- siteSqlUtils.updateApplicationPasswordCredentialsForWPAPISite(
1626- siteModel.url, username, password
1627- )
1628- }
1629- if (payload.apiRootUrl.isNotEmpty()) {
1630- siteSqlUtils.updateWpApiRestUrlForWPAPISite(siteModel.url, payload.apiRootUrl)
1631- }
1622+ persistAppPasswordColumns(
1623+ siteModel,
1624+ { username, password ->
1625+ siteSqlUtils.updateApplicationPasswordCredentialsForWPAPISite(
1626+ siteModel.url, username, password
1627+ )
1628+ },
1629+ { siteSqlUtils.updateWpApiRestUrlForWPAPISite(siteModel.url, it) }
1630+ )
16321631 }
16331632 result
16341633 } catch (e: Exception ) {
@@ -1693,6 +1692,32 @@ open class SiteStore @Inject constructor(
16931692 OnSiteChanged (siteSqlUtils.updateXmlRpcUrl(localId, xmlRpcUrl))
16941693 }
16951694
1695+ /* *
1696+ * Persists the application-password columns that the generic full-row write excludes — the encrypted
1697+ * credentials and, when present, [SiteModel.wpApiRestUrl] — via the supplied targeted writers, returning the
1698+ * number of rows written. The three post-write handlers (UPDATE_APPLICATION_PASSWORD, the WPAPI app-password
1699+ * fetch, and app-password XML-RPC login in createOrUpdateSites) differ only in targeting the row by local id
1700+ * or by URL, so each passes its matching writer pair.
1701+ *
1702+ * wpApiRestUrl is written only alongside credentials. A credential-less /me/sites model can be a WP.com simple
1703+ * site whose getWpApiRestUrl() synthesizes a public-api proxy URL; gating on credentials keeps that synthetic
1704+ * value out of the DB (such sites never carry app-password credentials).
1705+ */
1706+ private fun persistAppPasswordColumns (
1707+ site : SiteModel ,
1708+ persistCredentials : (username: String , password: String ) -> Int ,
1709+ persistWpApiRestUrl : (wpApiRestUrl: String ) -> Int
1710+ ): Int {
1711+ val username = site.apiRestUsernamePlain
1712+ val password = site.apiRestPasswordPlain
1713+ if (username.isNullOrEmpty() || password.isNullOrEmpty()) return 0
1714+ var rowsAffected = persistCredentials(username, password)
1715+ site.wpApiRestUrl?.takeIf { it.isNotEmpty() }?.let {
1716+ rowsAffected + = persistWpApiRestUrl(it)
1717+ }
1718+ return rowsAffected
1719+ }
1720+
16961721 @Suppress(" SwallowedException" , " TooGenericExceptionCaught" )
16971722 private fun updateApplicationPassword (siteModel : SiteModel ): OnSiteChanged {
16981723 return try {
@@ -1703,16 +1728,15 @@ open class SiteStore @Inject constructor(
17031728 } else {
17041729 // Existing site: credentials + WP_API_REST_URL are excluded from the full-row write, so
17051730 // persist them on the existing row via their targeted writers (nothing else changed).
1706- val username = siteModel.apiRestUsernamePlain
1707- val password = siteModel.apiRestPasswordPlain
1708- var rowsAffected = 0
1709- if (! username.isNullOrEmpty() && ! password.isNullOrEmpty()) {
1710- rowsAffected = siteSqlUtils.updateApplicationPasswordCredentials(siteFromDB.id, username, password)
1711- }
1712- siteModel.wpApiRestUrl?.takeIf { it.isNotEmpty() }?.let {
1713- rowsAffected = siteSqlUtils.updateWpApiRestUrl(siteFromDB.id, it)
1714- }
1715- OnSiteChanged (rowsAffected)
1731+ OnSiteChanged (
1732+ persistAppPasswordColumns(
1733+ siteModel,
1734+ { username, password ->
1735+ siteSqlUtils.updateApplicationPasswordCredentials(siteFromDB.id, username, password)
1736+ },
1737+ { siteSqlUtils.updateWpApiRestUrl(siteFromDB.id, it) }
1738+ )
1739+ )
17161740 }
17171741 } catch (e: DuplicateSiteException ) {
17181742 OnSiteChanged (SiteError (DUPLICATE_SITE ))
@@ -1737,8 +1761,9 @@ open class SiteStore @Inject constructor(
17371761 } else {
17381762 // Credentials + WP_API_REST_URL are excluded from the full-row write, so clear them on the
17391763 // existing row via their targeted writers now that the application password is gone.
1740- siteSqlUtils.clearApplicationPasswordCredentials(siteFromDB.id)
1741- OnSiteChanged (siteSqlUtils.clearWpApiRestUrl(siteFromDB.id))
1764+ val rowsAffected = siteSqlUtils.clearApplicationPasswordCredentials(siteFromDB.id) +
1765+ siteSqlUtils.clearWpApiRestUrl(siteFromDB.id)
1766+ OnSiteChanged (rowsAffected)
17421767 }
17431768 } catch (e: DuplicateSiteException ) {
17441769 OnSiteChanged (SiteError (DUPLICATE_SITE ))
@@ -1804,12 +1829,14 @@ open class SiteStore @Inject constructor(
18041829 // Credentials + wpApiRestUrl are excluded from the full-row write, so when this site carries
18051830 // app-password creds (XML-RPC app-password login), persist them on the row that was just
18061831 // written (localId) via the targeted writers.
1807- val apUsername = site.apiRestUsernamePlain
1808- val apPassword = site.apiRestPasswordPlain
1809- if (localId != 0 && ! apUsername.isNullOrEmpty() && ! apPassword.isNullOrEmpty()) {
1810- siteSqlUtils.updateApplicationPasswordCredentials(localId, apUsername, apPassword)
1811- site.wpApiRestUrl?.takeIf { it.isNotEmpty() }
1812- ?.let { siteSqlUtils.updateWpApiRestUrl(localId, it) }
1832+ if (localId != 0 ) {
1833+ persistAppPasswordColumns(
1834+ site,
1835+ { username, password ->
1836+ siteSqlUtils.updateApplicationPasswordCredentials(localId, username, password)
1837+ },
1838+ { siteSqlUtils.updateWpApiRestUrl(localId, it) }
1839+ )
18131840 }
18141841 } catch (caughtException: DuplicateSiteException ) {
18151842 duplicateSiteFound = true
@@ -2474,7 +2501,7 @@ open class SiteStore @Inject constructor(
24742501 val siteFromDB = getSiteByLocalId(siteModel.id)
24752502 ? : return OnSiteChanged (SiteError (SiteErrorType .INVALID_SITE ))
24762503 // Credentials are excluded from the full-row write, so clear them on the existing row via the
2477- // targeted writer. wpApiRestUrl is intentionally preserved here (see KDoc) — rotation reuses it.
2504+ // targeted writer. wpApiRestUrl is intentionally preserved here — credential rotation reuses it.
24782505 OnSiteChanged (siteSqlUtils.clearApplicationPasswordCredentials(siteFromDB.id))
24792506 } catch (e: DuplicateSiteException ) {
24802507 OnSiteChanged (SiteError (DUPLICATE_SITE ))
0 commit comments