Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 5 additions & 81 deletions ios/Sources/GutenbergKit/Sources/EditorConfiguration.swift
Copy link
Member

Choose a reason for hiding this comment

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

I feel we should address the incomplete cookie configuration referenced in #146 (comment). Would you be willing to address that in this PR or a follow-up PR?

Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ public struct EditorConfiguration {
public let namespaceExcludedPaths: [String]
/// Authorization header
public let authHeader: String
/// Global variables to be made available to the editor
public let webViewGlobals: [WebViewGlobal]
/// Raw block editor settings from the WordPress REST API
public let editorSettings: EditorSettings
public let editorSettings: String
/// Locale used for translations
public let locale: String
/// Endpoint for loading editor assets, used when enabling `shouldUsePlugins`
Expand All @@ -52,8 +50,7 @@ public struct EditorConfiguration {
siteApiNamespace: [String],
namespaceExcludedPaths: [String],
authHeader: String,
webViewGlobals: [WebViewGlobal],
editorSettings: EditorSettings,
editorSettings: String,
locale: String,
editorAssetsEndpoint: URL? = nil,
cookies: [HTTPCookie] = []
Expand All @@ -70,7 +67,6 @@ public struct EditorConfiguration {
self.siteApiNamespace = siteApiNamespace
self.namespaceExcludedPaths = namespaceExcludedPaths
self.authHeader = authHeader
self.webViewGlobals = webViewGlobals
self.editorSettings = editorSettings
self.locale = locale
self.editorAssetsEndpoint = editorAssetsEndpoint
Expand All @@ -91,7 +87,6 @@ public struct EditorConfiguration {
siteApiNamespace: siteApiNamespace,
namespaceExcludedPaths: namespaceExcludedPaths,
authHeader: authHeader,
webViewGlobals: webViewGlobals,
editorSettings: editorSettings,
locale: locale,
editorAssetsEndpoint: editorAssetsEndpoint
Expand All @@ -106,12 +101,6 @@ public struct EditorConfiguration {
content.addingPercentEncoding(withAllowedCharacters: .alphanumerics)!
}

var editorSettingsJSON: String {
// `editorSettings` values are always `encodable` so this should never fail
let jsonData = try! JSONSerialization.data(withJSONObject: editorSettings, options: [])
return String(data: jsonData, encoding: .utf8) ?? "undefined"
}

public static let `default` = EditorConfigurationBuilder().build()
}

Expand All @@ -128,8 +117,7 @@ public struct EditorConfigurationBuilder {
private var siteApiNamespace: [String]
private var namespaceExcludedPaths: [String]
private var authHeader: String
private var webViewGlobals: [WebViewGlobal]
private var editorSettings: EditorSettings
private var editorSettings: String
private var locale: String
private var editorAssetsEndpoint: URL?

Expand All @@ -146,8 +134,7 @@ public struct EditorConfigurationBuilder {
siteApiNamespace: [String] = [],
namespaceExcludedPaths: [String] = [],
authHeader: String = "",
webViewGlobals: [WebViewGlobal] = [],
editorSettings: EditorSettings = [:],
editorSettings: String = "undefined",
locale: String = "en",
editorAssetsEndpoint: URL? = nil
){
Expand All @@ -163,7 +150,6 @@ public struct EditorConfigurationBuilder {
self.siteApiNamespace = siteApiNamespace
self.namespaceExcludedPaths = namespaceExcludedPaths
self.authHeader = authHeader
self.webViewGlobals = webViewGlobals
self.editorSettings = editorSettings
self.locale = locale
self.editorAssetsEndpoint = editorAssetsEndpoint
Expand Down Expand Up @@ -241,13 +227,7 @@ public struct EditorConfigurationBuilder {
return copy
}

public func setWebViewGlobals(_ webViewGlobals: [WebViewGlobal]) -> EditorConfigurationBuilder {
var copy = self
copy.webViewGlobals = webViewGlobals
return copy
}

public func setEditorSettings(_ editorSettings: EditorSettings) -> EditorConfigurationBuilder {
public func setEditorSettings(_ editorSettings: String) -> EditorConfigurationBuilder {
var copy = self
copy.editorSettings = editorSettings
return copy
Expand Down Expand Up @@ -279,69 +259,13 @@ public struct EditorConfigurationBuilder {
siteApiNamespace: siteApiNamespace,
namespaceExcludedPaths: namespaceExcludedPaths,
authHeader: authHeader,
webViewGlobals: webViewGlobals,
editorSettings: editorSettings,
locale: locale,
editorAssetsEndpoint: editorAssetsEndpoint
)
}
}

public struct WebViewGlobal: Equatable {
let name: String
let value: WebViewGlobalValue

public init(name: String, value: WebViewGlobalValue) throws {
// Validate name is a valid JavaScript identifier
guard Self.isValidJavaScriptIdentifier(name) else {
throw WebViewGlobalError.invalidIdentifier(name)
}
self.name = name
self.value = value
}

private static func isValidJavaScriptIdentifier(_ name: String) -> Bool {
// Add validation logic for JavaScript identifiers
return name.range(of: "^[a-zA-Z_$][a-zA-Z0-9_$]*$", options: .regularExpression) != nil
}
}

public enum WebViewGlobalError: Error {
case invalidIdentifier(String)
}

public enum WebViewGlobalValue: Equatable {
case string(String)
case number(Double)
case boolean(Bool)
case object([String: WebViewGlobalValue])
case array([WebViewGlobalValue])
case null

func toJavaScript() -> String {
switch self {
case .string(let str):
return "\"\(str.escaped)\""
case .number(let num):
return "\(num)"
case .boolean(let bool):
return "\(bool)"
case .object(let dict):
let sortedKeys = dict.keys.sorted()
var pairs: [String] = []
for key in sortedKeys {
let value = dict[key]!
pairs.append("\"\(key.escaped)\": \(value.toJavaScript())")
}
return "{\(pairs.joined(separator: ","))}"
case .array(let array):
return "[\(array.map { $0.toJavaScript() }.joined(separator: ","))]"
case .null:
return "null"
}
}
}

public typealias EditorSettings = [String: Encodable]

// String escaping extension
Expand Down
8 changes: 1 addition & 7 deletions ios/Sources/GutenbergKit/Sources/EditorViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,7 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro

private func getEditorConfiguration() -> WKUserScript {

// Generate JavaScript globals
let globalsJS = configuration.webViewGlobals.map { global in
"window[\"\(global.name)\"] = \(global.value.toJavaScript());"
}.joined(separator: "\n")

let jsCode = """
\(globalsJS)

window.GBKit = {
siteURL: '\(configuration.siteURL)',
Expand All @@ -162,7 +156,7 @@ public final class EditorViewController: UIViewController, GutenbergEditorContro
authHeader: '\(configuration.authHeader)',
themeStyles: \(configuration.shouldUseThemeStyles),
hideTitle: \(configuration.shouldHideTitle),
editorSettings: \(configuration.editorSettingsJSON),
editorSettings: \(configuration.editorSettings),
locale: '\(configuration.locale)',
post: {
id: \(configuration.postID ?? -1),
Expand Down
29 changes: 8 additions & 21 deletions ios/Tests/GutenbergKitTests/EditorConfigurationBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ struct EditorConfigurationBuilderTests {
#expect(builder.siteApiNamespace == [])
#expect(builder.namespaceExcludedPaths == [])
#expect(builder.authHeader == "")
#expect(builder.webViewGlobals == [])
#expect(builder.editorSettings.isEmpty)
#expect(builder.editorSettings == "undefined")
#expect(builder.locale == "en")
#expect(builder.editorAssetsEndpoint == nil)
}

@Test("Editor Configuration to Builder")
func testThatEditorConfigurationToBuilder() throws {
let configuration = try EditorConfigurationBuilder()
let configuration = EditorConfigurationBuilder()
.setTitle("Title")
.setContent("Content")
.setPostID(123)
Expand All @@ -41,8 +40,7 @@ struct EditorConfigurationBuilderTests {
.setSiteApiNamespace(["wp", "v2"])
.setNamespaceExcludedPaths(["jetpack"])
.setAuthHeader("Bearer Token")
.setWebViewGlobals([WebViewGlobal(name: "foo", value: .string("bar"))])
.setEditorSettings(["foo":"bar"])
.setEditorSettings(#"{"foo":"bar"}"#)
.setLocale("fr")
.setEditorAssetsEndpoint(URL(string: "https://example.com/wp-content/plugins/gutenberg/build/"))
.build() // Convert to a configuration
Expand All @@ -61,8 +59,7 @@ struct EditorConfigurationBuilderTests {
#expect(configuration.siteApiNamespace == ["wp", "v2"])
#expect(configuration.namespaceExcludedPaths == ["jetpack"])
#expect(configuration.authHeader == "Bearer Token")
#expect(configuration.webViewGlobals == [try WebViewGlobal(name: "foo", value: .string("bar"))])
#expect(configuration.editorSettingsJSON == #"{"foo":"bar"}"#)
#expect(configuration.editorSettings == #"{"foo":"bar"}"#)
#expect(configuration.locale == "fr")
#expect(configuration.editorAssetsEndpoint == URL(string: "https://example.com/wp-content/plugins/gutenberg/build/"))
}
Expand Down Expand Up @@ -138,25 +135,15 @@ struct EditorConfigurationBuilderTests {
#expect(EditorConfigurationBuilder().setAuthHeader("Bearer token").build().authHeader == "Bearer token")
}

@Test("Sets webViewGlobals Correctly")
func editorConfigurationBuilderSetsWebViewGlobalsCorrectly() throws {
#expect(
try EditorConfigurationBuilder()
.setWebViewGlobals([WebViewGlobal(name: "foo", value: .string("bar"))])
.build()
.webViewGlobals
== [WebViewGlobal(name: "foo", value: .string("bar"))]
)
}

@Test("Sets editorSettings Correctly")
func editorConfigurationBuilderSetsEditorSettingsCorrectly() throws {
let json = #"{"foo":"bar"}"#
#expect(
EditorConfigurationBuilder()
.setEditorSettings(["foo": "bar"])
.setEditorSettings(json)
.build()
.editorSettingsJSON
== "{\"foo\":\"bar\"}"
.editorSettings
== json
)
}

Expand Down
115 changes: 0 additions & 115 deletions ios/Tests/GutenbergKitTests/EditorConfigurationTests.swift

This file was deleted.