Conversation
Generated by 🚫 Danger |
2a49241 to
7d72605
Compare
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These are replaced by the simplified GutenbergKitSettingsBuilder and the new GutenbergEditorPreloader in subsequent commits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inline the 18-parameter buildEditorConfiguration into buildPostConfiguration, which now reads site/post fields directly from SiteModel and PostImmutableModel. Also fixes a bug in shouldUsePlugins where a stray `return false` made the method always return false. Tests are updated to construct SiteModel objects instead of passing individual primitives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the old GutenbergKitConfig builder pattern with a simpler buildEditorConfiguration that calls buildPostConfiguration and customizes locale, cookies, plugins, theme styles, and network logging via toBuilder(). Remove the ProgressBar from the editor layout since GutenbergView manages its own loading visibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Preload editor dependencies (settings JSON, JS/CSS assets, API preload data) in the background when the My Site screen loads, so they are ready when the user opens the editor. GutenbergEditorPreloader is a Dagger singleton that guards against redundant work and caches results. The cache is cleared on pull-to-refresh, which triggers a fresh preload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire the fragment for Dagger injection via AppComponent so it can read preloaded dependencies directly from GutenbergEditorPreloader instead of receiving them through Bundle arguments. Also use FrameLayout.LayoutParams when adding GutenbergView to its container. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7d72605 to
e221a2a
Compare
Project dependencies changeslist+ New Dependencies
org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3
! Upgraded Dependencies
org.wordpress.gutenbergkit:android:316-34bc052ce3435b4189a4cbcec18d5d7b63ee34b3, (changed from v0.11.1)tree++--- androidx.navigation:navigation-compose:2.9.7
+| \--- androidx.navigation:navigation-compose-android:2.9.7
+| \--- androidx.activity:activity:1.8.0 -> 1.10.1
+| \--- androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.1 -> 2.10.0
+| \--- androidx.lifecycle:lifecycle-viewmodel-savedstate-android:2.10.0
+| \--- androidx.savedstate:savedstate:1.4.0
+| \--- androidx.savedstate:savedstate-android:1.4.0
+| \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3
+| \--- org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:1.7.3
+| \--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3
+| +--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3 (c)
+| \--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3 (c)
+--- project :libs:editor
-| \--- org.wordpress.gutenbergkit:android:v0.11.1
-| +--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.21 -> 2.2.21 (*)
-| +--- androidx.core:core-ktx:1.13.1 -> 1.16.0 (*)
-| +--- androidx.appcompat:appcompat:1.7.0 -> 1.7.1 (*)
-| +--- com.google.android.material:material:1.12.0 (*)
-| +--- androidx.webkit:webkit:1.11.0 -> 1.15.0 (*)
-| +--- com.google.code.gson:gson:2.8.9 -> 2.13.2
-| | \--- com.google.errorprone:error_prone_annotations:2.41.0
-| +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 (*)
-| \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 -> 2.2.21 (*)
+| \--- org.wordpress.gutenbergkit:android:316-34bc052ce3435b4189a4cbcec18d5d7b63ee34b3
+| +--- org.jetbrains.kotlin:kotlin-parcelize-runtime:2.0.21 -> 2.2.21 (*)
+| +--- androidx.core:core-ktx:1.13.1 -> 1.16.0 (*)
+| +--- androidx.appcompat:appcompat:1.7.0 -> 1.7.1 (*)
+| +--- com.google.android.material:material:1.12.0 (*)
+| +--- androidx.webkit:webkit:1.11.0 -> 1.15.0 (*)
+| +--- com.google.code.gson:gson:2.8.9 -> 2.13.2
+| | \--- com.google.errorprone:error_prone_annotations:2.41.0
+| +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2 (*)
+| +--- org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.7.3
+| | +--- org.jetbrains.kotlinx:kotlinx-serialization-bom:1.7.3 (*)
+| | +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.20 -> 2.2.21 (*)
+| | \--- org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3 (*)
+| +--- org.jsoup:jsoup:1.18.1 -> 1.22.1
+| +--- com.squareup.okhttp3:okhttp:4.12.0 -> 5.3.2 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.21 -> 2.2.21 (*)
-\--- org.wordpress.gutenbergkit:android:v0.11.1 (*)
+\--- org.wordpress.gutenbergkit:android:316-34bc052ce3435b4189a4cbcec18d5d7b63ee34b3 (*) |
|
|
WordPress/src/main/java/org/wordpress/android/ui/posts/editor/GutenbergKitEditorFragment.kt
Fixed
Show fixed
Hide fixed
|
|
Local draft posts have remotePostId defaulting to 0, which was passed directly to GutenbergKit's setPostId(). GBK expects null for unsaved posts. Now check isLocalDraft and pass null instead.
…utenbergKitEditorFragment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pper Per-site boolean preference that caches whether a site supports the wp-block-editor/v1/settings endpoint, used by EditorSettingsRepository. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable the actual editor settings support check (was returning false) and persist results to AppPrefs. Add logging at every key point for debugging: cache hits, API fetch results, and persistence. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inject EditorSettingsRepository so the builder can query editor settings support when configuring theme styles. Extract buildCachedHosts and buildEditorAssetsEndpoint into named methods. Update GutenbergKitActivity to use the injected instance and remove redundant builder overrides. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GutenbergEditorPreloader now fetches editor settings support before building the editor configuration, ensuring the config reflects the latest server state. GutenbergKitEditorFragment uses the preloaded dependencies instead of passing null. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check for both wp-block-editor/v1/settings and wpcom/v2/editor-assets routes in a single API root request via fetchEditorCapabilitiesForSite. Add getSupportsEditorAssetsForSite for reading the cached result. Change all new pref methods to take SiteModel instead of a raw ID to prevent misuse of siteId vs local ID. The prefs key internally using the local database ID which is always unique across site types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fetch current theme during editor capability detection to determine if it is a block theme, persisting the result alongside editor settings and editor assets support. In Site Settings, show contextual messages under "Use Theme Styles" when the site lacks editor settings support (disabled) or when the active theme is not a block theme. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Read the actual checkbox value from the local site settings DB instead of just checking whether the site supports editor settings. Falls back to false when editor settings are not supported. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new site setting that lets users control whether third-party blocks and styles from installed plugins are loaded in the editor. The setting is gated behind GutenbergKit being enabled and the site supporting editor assets. Includes DB migration, settings UI toggle, and integration with GutenbergKitSettingsBuilder. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `isWPComSimpleSite()` to SiteModel and use it to route WP.com simple sites through the public-api.wordpress.com proxy with bearer token authentication. This enables the Rust WP API client to make authenticated requests for WPCom sites that don't use application passwords. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove the requirement for a non-empty namespace when building the editor assets endpoint URL. Self-hosted sites have an empty namespace but the wpcom/v2/editor-assets endpoint is still valid for them. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace direct SiteSettingsTable.getSettings() calls in GutenbergKitSettingsBuilder with an injectable SiteSettingsProvider that returns SiteSettingsModel directly, encapsulating cursor lifecycle in the implementation. Also fixes three pre-existing test failures: editor assets endpoint tests now expect a URL for self-hosted sites, and the app-password test uses a Jetpack-connected site (the realistic scenario). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove unused dependencies field and read() method from EditorDependencyStore. Add checkstyle suppression comments for WpComDotOrgApiUrlResolver references in WpApiClientProvider. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EditorDependencyStore was a stateless wrapper around EditorService with a single call site. Inline the two calls directly and delete the class. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single-site fields with a ConcurrentHashMap of sealed PreloadState entries (Loading | Ready) keyed by site local ID. This fixes a bug where switching sites discarded previously preloaded dependencies, and eliminates the need for withContext(mainDispatcher) thread-hopping. Add @mainthread annotations to all public methods so Android Lint flags any call-site violations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Accept SiteModel instead of nothing, store the site local ID as a fragment argument, and use it to look up preloaded dependencies from the multi-site cache in GutenbergEditorPreloader. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WordPress/src/test/java/org/wordpress/android/repositories/ThemeRepositoryTest.kt
Fixed
Show fixed
Hide fixed
WordPress/src/test/java/org/wordpress/android/repositories/ThemeRepositoryTest.kt
Fixed
Show fixed
Hide fixed
WordPress/src/test/java/org/wordpress/android/repositories/ThemeRepositoryTest.kt
Fixed
Show fixed
Hide fixed
Inline the logic from SiteUtils.isBlockEditorDefaultForNewPost into SiteSettingsProviderImpl so the preloader can use it via injection rather than a static call. Deprecate the old static methods and suppress warnings at the three remaining call sites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap the static EditorService.create() + prepare() calls behind an injectable interface so GutenbergEditorPreloader can be tested with a mock. Wire the binding through Hilt in PostModule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
19 tests covering: feature gating, successful preload, failure cleanup, deduplication (completed and in-flight), scope cancellation recovery, multi-site caching, refresh, and clear semantics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The custom DoNotMockDataClass lint rule flags mocking of data classes. Construct real ThemeWithEditContext instances via a buildTheme() helper instead. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Six tests covering the isBlockEditorDefault decision matrix: null/empty editor, gutenberg editor, non-gutenberg on self-hosted, WPCom simple, and WPCom Atomic. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WordPress/src/test/java/org/wordpress/android/ui/posts/GutenbergEditorPreloaderTest.kt
Fixed
Show fixed
Hide fixed
WordPress/src/test/java/org/wordpress/android/ui/posts/GutenbergEditorPreloaderTest.kt
Fixed
Show fixed
Hide fixed
DoNotMockDataClass lint rule flagged the @mock annotation on EditorDependencies. Use EditorDependencies.empty for the shared field and construct a real instance for the refresh test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## trunk #22579 +/- ##
==========================================
+ Coverage 38.18% 38.22% +0.04%
==========================================
Files 2238 2241 +3
Lines 111772 111864 +92
Branches 15585 15598 +13
==========================================
+ Hits 42680 42762 +82
- Misses 65551 65553 +2
- Partials 3541 3549 +8 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Point to GutenbergKit PR #316 build artifact for WP Android integration support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
dcalhoun
left a comment
There was a problem hiding this comment.
Thank you for implementing this! 🙇🏻♂️
I was unable to fully test the implementation due to the issues noted in inline comments and in wordpress-mobile/GutenbergKit#316 (review). They kept me from truly experiencing the intended functionality.
In addition to the inline comments here, I was unable to upload new media or attach Media Library items to block. When I attempted to do so, the media never displayed in the block editor canvas, the block remained as a placeholder. There were no logs in the Android Studio or Chrome console. 😕
Hopefully we can identify the root cause for some of these foundational problems and I can perform deeper testing afterwards.
| ) | ||
| } else { | ||
| WpAuthenticationProvider.staticWithUsernameAndPassword( | ||
| username = site.apiRestUsernamePlain, |
There was a problem hiding this comment.
I encountered a crash when opening the editor for the time with a Wow/Atomic site. GBK and plugins features were enabled. Before opening the editor, I created an application password for the site via tapping the prompt atop the My Site view.
Stack trace
FATAL EXCEPTION: DefaultDispatcher-worker-6 (Fix with AI)
Process: com.jetpack.android.beta, PID: 3797
java.lang.NullPointerException: getApiRestUsernamePlain(...) must not be null
at org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider.getWpApiClient(WpApiClientProvider.kt:43)
at org.wordpress.android.fluxc.network.rest.wpapi.rs.WpApiClientProvider.getWpApiClient$default(WpApiClientProvider.kt:32)
at org.wordpress.android.repositories.ThemeRepository$fetchCurrentTheme$2.invokeSuspend(ThemeRepository.kt:27)
at org.wordpress.android.repositories.ThemeRepository$fetchCurrentTheme$2.invoke(Unknown Source:8)
at org.wordpress.android.repositories.ThemeRepository$fetchCurrentTheme$2.invoke(Unknown Source:4)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndspatched(Undispatched.kt:66)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:43)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:165)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
at org.wordpress.android.repositories.ThemeRepository.fetchCurrentTheme(ThemeRepository.kt:26)
at org.wordpress.android.repositories.EditorSettingsRepository.fetchThemeBlockStyleSupport(EditorSettingsRepository.kt:162)
at org.wordpress.android.repositories.EditorSettingsRepository.access$fetchThemeBlockStyleSupport(EditorSettingsRepository.kt:19)
at org.wordpress.android.repositories.EditorSettingsRepository$fetchEditorCapabilitiesForSite$2$1$2.invokeSuspend(EditorSettingsRepository.kt:110)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:124)
at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:89)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:586)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:820)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:717)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:704)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@4908726, Dispatchers.IO]
This did not occur in the prototype build, only a local build. After it occurred, I app continued to crash on launch, presumably the same site remains selected on My Site.
| mUseThirdPartyBlocksPref = | ||
| (WPSwitchPreference) getChangePref(R.string.pref_key_use_third_party_blocks); | ||
| mUseThirdPartyBlocksPref.setChecked(mSiteSettings.getUseThirdPartyBlocks()); |
There was a problem hiding this comment.
Additionally, when I enabled this option for a newly added jetpack.wpmt.co site, I encountered a crash.
Stack trace
FATAL EXCEPTION: main (Fix with AI)
Process: com.jetpack.android.beta, PID: 30058
java.lang.AssertionError: Cannot get asset path from empty bundle
at org.wordpress.gutenberg.model.EditorAssetBundle.assetDataPath(EditorAssetBundle.kt:137)
at org.wordpress.gutenberg.model.EditorAssetBundle.hasAssetData(EditorAssetBundle.kt:120)
at org.wordpress.gutenberg.CachedAssetRequestInterceptor.handleRequest(CachedAssetRequestInterceptor.kt:48)
at org.wordpress.gutenberg.GutenbergView$initializeWebView$1.shouldInterceptRequest(GutenbergView.kt:395)
at WV.og.a(chromium-SystemWebViewGoogle6432.aab-stable-755913303:101)
at org.chromium.android_webview.ShouldInterceptRequestMediator.shouldInterceptRequestFromNative(chromium-SystemWebViewGoogle6432.aab-stable-755913303:18)
Using the pull-to-refresh gesture on My Site seems to address it. I was able to then open the editor and see Jetpack blocks.
| if (isWPComSimpleSite()) { | ||
| return "https://public-api.wordpress.com/wp/v2/sites/" | ||
| + mSiteId; | ||
| } |
There was a problem hiding this comment.
I asked Claud to assess if this might introduce unexpected behavior elsewhere if existing callers expect a null value. It seems to think it is OK.
Claude's findings
Summary
┌──────────┬────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────┐
│ Severity │ File │ Issue │
├──────────┼────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Medium │ CookieNonceAuthenticator │ Null-check-based discovery/retry logic broken for WP.com simple sites │
├──────────┼────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Medium │ ReactNativeStore │ Same null-check-based discovery/retry logic broken │
├──────────┼────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Low │ WpApiClientProvider.buildUrl() │ Intentional change, but affects getWpApiClientCookiesNonceAuthentication() │
├──────────┼────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ None │ All other callers │ Either write-only, guarded by isUsingSelfHostedRestApi, or in the PR itself │
└──────────┴────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────┘
Recommendation
The practical risk is low because CookieNonceAuthenticator and ReactNativeStore are only used for self-hosted/cookie-nonce auth flows, and WP.com simple
sites shouldn't reach those code paths. However, the semantic change to the getter is a landmine for future code — anyone writing if (site.wpApiRestUrl !=
null) as a guard for "has a real persisted REST URL" will silently get wrong behavior for WP.com simple sites.
Consider either:
1. Adding a separate method like getEffectiveWpApiRestUrl() for the synthesized URL and leaving getWpApiRestUrl() as a pure field accessor, or
2. Adding a hasPersistedWpApiRestUrl() method that checks the raw field, so callers that need to distinguish "synthesized" from "persisted" can do so
explicitly.
| private fun getUseThirdPartyBlocks(site: SiteModel): Boolean { | ||
| if (!editorSettingsRepository | ||
| .getSupportsEditorAssetsForSite(site) | ||
| ) { | ||
| return false | ||
| } | ||
| return siteSettingsProvider | ||
| .getSettings(site)?.useThirdPartyBlocks ?: false | ||
| } |
There was a problem hiding this comment.
Should we have this continue to respect the remote gutenberg_kit_plugins feature flag?
| // hide third-party blocks preference if GutenbergKit is not enabled | ||
| if (!mGutenbergKitFeatureChecker.isGutenbergKitEnabled()) { | ||
| WPPrefUtils.removePreference( | ||
| this, R.string.pref_key_site_editor, R.string.pref_key_use_third_party_blocks | ||
| ); |
There was a problem hiding this comment.
Should we also hide this if the remote gutenberg_kit_plugins feature flag is disabled?
| <string name="site_settings_use_theme_styles_unsupported">Install the Gutenberg Plugin on your site to activate theme style support.</string> | ||
| <string name="site_settings_use_theme_styles_not_block_theme">Your site isn\'t using a Block Theme, so the editor might not match your content correctly. If things aren\'t looking right, you can disable editor styles.</string> | ||
| <string name="site_settings_use_third_party_blocks">Use Third-Party Blocks (Beta)</string> | ||
| <string name="site_settings_use_third_party_blocks_summary">Load third-party blocks and styles from plugins installed on your site.</string> |
There was a problem hiding this comment.
Maybe styles is superfluous? Blocks include the styles. Are there editor plugins that load styles without blocks? Does it impact GBK?
| <string name="site_settings_use_third_party_blocks_summary">Load third-party blocks and styles from plugins installed on your site.</string> | |
| <string name="site_settings_use_third_party_blocks_summary">Load third-party blocks from plugins installed on your site.</string> |
| gutenbergView.setLatestContentProvider( | ||
| object : GutenbergView.LatestContentProvider { | ||
| override fun getLatestContent(): | ||
| GutenbergView.LatestContent? { | ||
| return null | ||
| } | ||
| } | ||
| } | ||
| ) |
There was a problem hiding this comment.
Is this a non-functioning placeholder for integrating #22493?






Description
Adds background preloading of GutenbergKit editor dependencies so the block editor opens faster. When the My Site dashboard loads,
GutenbergEditorPreloaderfetches editor capabilities, theme styles, and third-party block settings on a background thread. If preloading completes before the editor is opened, the cached result is used immediately; otherwise the editor loads dependencies itself.Key changes:
GutenbergEditorPreloadersingleton driven byMySiteViewModelthat opportunistically warmsEditorDependencyStorein the background. Supports idempotent preload, forced refresh (pull-to-refresh), and graceful cancellation.EditorSettingsRepositoryandThemeRepositoryfor fetching editor capabilities and active theme data via WP API. Per-site preferences track whether the site supports editor settings and editor assets.objectto an injectable@Singletonclass. Now reads "Use Theme Styles" and "Use Third-Party Blocks" from site settings to configure the editor. Builds an editor assets endpoint for both WP.com and self-hosted sites.WpApiClientProviderandSiteModel.getWpApiRestUrl()now handle WP.com simple sites that lack a self-hosted REST URL.Testing instructions
Editor preloading on WP.com site:
Pull-to-refresh clears preloaded dependencies:
Editor configuration for self-hosted site with application password:
Theme styles and third-party blocks settings:
Local draft post ID handling:
I tested against the following sites: