Skip to content
Draft
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
2 changes: 0 additions & 2 deletions Modules/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,7 @@ let package = Package(
"FormattableContentKit",
"SFHFKeychainUtils",
"WordPressShared",
"WordPressSharedUI",
"WordPressKit",
"WordPressUI",
.product(name: "CocoaLumberjack", package: "CocoaLumberjack"),
.product(name: "CocoaLumberjackSwift", package: "CocoaLumberjack"),
.product(name: "Gravatar", package: "Gravatar-SDK-iOS"),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import CoreData
import WordPressKit
import WordPressShared
import WordPressSharedUI

extension ReaderPost {
/// Finds an existing `ReaderPost` matching the given `globalID` and `topic`,
Expand Down
1 change: 0 additions & 1 deletion Modules/Sources/WordPressData/Swift/ManagedPerson.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Foundation
import CoreData
import WordPressKit
import WordPressUI
import Gravatar

public typealias Person = RemotePerson
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import Foundation
import UIKit
import WordPressShared
import WordPressSharedObjCUI

/// Contains methods for formatting post or comment content for display.
///
Expand All @@ -28,7 +25,7 @@ import WordPressSharedObjCUI
static let styleAttr = try! NSRegularExpression(pattern: "\\s*style=\"[^\"]*\"", options: .caseInsensitive)

// Gallery Images
static let galleryImgTags = try! NSRegularExpression(pattern: "<img[^>]*data-orig-file[^>]*/>", options: .caseInsensitive)
public static let galleryImgTags = try! NSRegularExpression(pattern: "<img[^>]*data-orig-file[^>]*/>", options: .caseInsensitive)

// Trailing BR Tags
static let trailingBRTags = try! NSRegularExpression(pattern: "(\\s*<br\\s*(/?)\\s*>\\s*)+$", options: .caseInsensitive)
Expand All @@ -38,31 +35,6 @@ import WordPressSharedObjCUI
static let gutenbergGalleryListItem = try! NSRegularExpression(pattern: "<li[^>]+gallery-item[^>]+>(<figure><img .+?</figure>)</li>", options: .caseInsensitive)
}

/// Formats the specified content string for display. Forbidden HTML tags are
/// removed, paragraphs are normalized, etc.
///
/// - Parameters:
/// - string: The content string to format.
/// - isPrivate: Whether the content is from a private blog.
///
/// - Returns: The formatted string.
///
@objc public class func formatContentString(_ string: String, isPrivateSite isPrivate: Bool) -> String {
guard !string.isEmpty else {
return string
}

var content = string
content = removeForbiddenTags(content)
content = normalizeParagraphs(content)
content = removeInlineStyles(content)
content = (content as NSString).replacingHTMLEmoticonsWithEmoji() as String
content = formatGutenbergGallery(content)
content = resizeGalleryImageURL(content, isPrivateSite: isPrivate)
content = formatVideoTags(content)
return content
}

/// Removes forbidden HTML tags from the specified string.
///
/// - Parameters:
Expand Down Expand Up @@ -197,59 +169,6 @@ import WordPressSharedObjCUI
return content
}

/// Mutates gallery image URLs to be correctly sized.
///
/// - Parameters:
/// - string: The content string to format.
/// - isPrivate: Whether the content is from a private blog.
///
/// - Returns: The formatted string.
///
@objc public class func resizeGalleryImageURL(_ string: String, isPrivateSite isPrivate: Bool) -> String {
guard !string.isEmpty else {
return string
}

let imageSize = UIScreen.main.bounds.size
let scale = UIScreen.main.scale
let scaledSize = imageSize.applying(CGAffineTransform(scaleX: scale, y: scale))

let mContent = NSMutableString(string: string)

let matches = RegEx.galleryImgTags.matches(in: mContent as String, options: [], range: NSRange(location: 0, length: mContent.length))

for match in matches.reversed() {
let imgElementStr = mContent.substring(with: match.range)
let srcImgURLStr = parseValueForAttribute("src", inElement: imgElementStr)
let originalImgURLStr = parseValueForAttribute("data-orig-file", inElement: imgElementStr)

guard let originalURL = URL(string: originalImgURLStr) else {
continue
}

var modifiedURL: URL
if isPrivate {
modifiedURL = WPImageURLHelper.imageURLWithSize(scaledSize, forImageURL: originalURL)
} else {
modifiedURL = PhotonImageURLHelper.photonURL(with: imageSize, forImageURL: originalURL)
}

guard modifiedURL.absoluteString.isEmpty() == false else {
continue
}

let mImageStr = NSMutableString(string: imgElementStr)
mImageStr.replaceOccurrences(of: srcImgURLStr,
with: modifiedURL.absoluteString,
options: .literal,
range: NSRange(location: 0, length: imgElementStr.count))

mContent.replaceCharacters(in: match.range, with: mImageStr as String)
}

return mContent as String
}

/// Parses the specified string for the value of the specified attribute.
///
/// - Parameters:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import Foundation
import UIKit
import WordPressShared
import WordPressSharedObjCUI

/// UIKit-dependent additions to `RichContentFormatter`.
///
/// The platform-independent text transformations live in `WordPressShared`. The
/// display pipeline below depends on the screen (for gallery image sizing) and on
/// `WordPressSharedObjCUI`'s Photon helper, so it stays in the UI layer.
///
extension RichContentFormatter {

/// Formats the specified content string for display. Forbidden HTML tags are
/// removed, paragraphs are normalized, etc.
///
/// - Parameters:
/// - string: The content string to format.
/// - isPrivate: Whether the content is from a private blog.
///
/// - Returns: The formatted string.
///
@objc public class func formatContentString(_ string: String, isPrivateSite isPrivate: Bool) -> String {
guard !string.isEmpty else {
return string
}

var content = string
content = removeForbiddenTags(content)
content = normalizeParagraphs(content)
content = removeInlineStyles(content)
content = (content as NSString).replacingHTMLEmoticonsWithEmoji() as String
content = formatGutenbergGallery(content)
content = resizeGalleryImageURL(content, isPrivateSite: isPrivate)
content = formatVideoTags(content)
return content
}

/// Mutates gallery image URLs to be correctly sized.
///
/// - Parameters:
/// - string: The content string to format.
/// - isPrivate: Whether the content is from a private blog.
///
/// - Returns: The formatted string.
///
@objc public class func resizeGalleryImageURL(_ string: String, isPrivateSite isPrivate: Bool) -> String {
guard !string.isEmpty else {
return string
}

let imageSize = UIScreen.main.bounds.size
let scale = UIScreen.main.scale
let scaledSize = imageSize.applying(CGAffineTransform(scaleX: scale, y: scale))

let mContent = NSMutableString(string: string)

let matches = RegEx.galleryImgTags.matches(
in: mContent as String,
options: [],
range: NSRange(location: 0, length: mContent.length)
)

for match in matches.reversed() {
let imgElementStr = mContent.substring(with: match.range)
let srcImgURLStr = parseValueForAttribute("src", inElement: imgElementStr)
let originalImgURLStr = parseValueForAttribute("data-orig-file", inElement: imgElementStr)

guard let originalURL = URL(string: originalImgURLStr) else {
continue
}

var modifiedURL: URL
if isPrivate {
modifiedURL = WPImageURLHelper.imageURLWithSize(scaledSize, forImageURL: originalURL)
} else {
modifiedURL = PhotonImageURLHelper.photonURL(with: imageSize, forImageURL: originalURL)
}

guard modifiedURL.absoluteString.isEmpty() == false else {
continue
}

let mImageStr = NSMutableString(string: imgElementStr)
mImageStr.replaceOccurrences(
of: srcImgURLStr,
with: modifiedURL.absoluteString,
options: .literal,
range: NSRange(location: 0, length: imgElementStr.count)
)

mContent.replaceCharacters(in: match.range, with: mImageStr as String)
}

return mContent as String
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import XCTest
@testable import WordPressShared
@testable import WordPressSharedUI

class RichContentFormatterTests: XCTestCase {

Expand Down Expand Up @@ -32,10 +31,6 @@ class RichContentFormatterTests: XCTestCase {
XCTAssertTrue(str == sanitizedStr, "Not all paragraphs were normalized.")
}

func testResizeGalleryImageURLsForContentEmptyString() {
XCTAssertTrue("" == RichContentFormatter.resizeGalleryImageURL("", isPrivateSite: false))
}

func testRemoveTrailingBRTags() {
let str = "<p>test</p><br><p>test</p>"
let styleStr = "<p>test</p><br><p>test</p><br><br> "
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import XCTest
@testable import WordPressShared
@testable import WordPressSharedUI

class RichContentFormatterUITests: XCTestCase {

func testResizeGalleryImageURLsForContentEmptyString() {
XCTAssertTrue("" == RichContentFormatter.resizeGalleryImageURL("", isPrivateSite: false))
}
}
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ let package = Package(
path: "Modules/Tests/WordPressSharedTests",
exclude: [
"WordPressShared.xctestplan",
"RichContentFormatterTests.swift",
"RichContentFormatterUITests.swift",
"WPUserAgentTests.swift"
],
swiftSettings: [.swiftLanguageMode(.v5)]
Expand Down