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

[feature/suffix-protection] File extension / suffix protection #1298

Merged
merged 5 commits into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
438 changes: 236 additions & 202 deletions CHANGELOG.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion ios-sdk
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ class CreateDocumentAction: Action {
}
})

documentNameViewController.requiredFileExtension = fileType.extension
documentNameViewController.navigationItem.title = "Pick a name".localized

navigationViewController.pushViewController(documentNameViewController, animated: true)
Expand Down
15 changes: 13 additions & 2 deletions ownCloud/Client/Actions/Scanner/ScanViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ class ScanViewController: StaticTableViewController {
didSet {
if let fileName = fileNameRow?.value as? NSString {
fileNameRow?.value = fileName.deletingPathExtension + "." + (exportFormat?.suffix ?? "")
fileNameRow?.requiredFileExtension = exportFormat?.suffix

if let textField = fileNameRow?.textField, textField.isFirstResponder {
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.beginningOfDocument)
}
}

guard let oneFilePerPageRow = oneFilePerPageRow else { return }
Expand Down Expand Up @@ -315,7 +320,9 @@ class ScanViewController: StaticTableViewController {
self.isModalInPresentation = true
self.navigationItem.title = "Scan".localized
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(ScanViewController.cancel))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(ScanViewController.save))

let saveBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(ScanViewController.save))
self.navigationItem.rightBarButtonItem = saveBarButtonItem

self.core = core
self.targetFolderItem = item
Expand Down Expand Up @@ -353,7 +360,7 @@ class ScanViewController: StaticTableViewController {
// Save section

// - Name
fileNameRow = StaticTableViewRow(textFieldWithAction: { [weak self] (row, textField, type) in
fileNameRow = StaticTableViewRow(textFieldWithAction: { [weak self, weak saveBarButtonItem] (row, textField, type) in
self?.navigationItem.rightBarButtonItem?.isEnabled = ((row.value as? String)?.count ?? 0) > 0

if type == .didBegin, let nameTextField = textField as? UITextField {
Expand All @@ -369,6 +376,10 @@ class ScanViewController: StaticTableViewController {
nameTextField.selectedTextRange = nameTextField.textRange(from: nameTextField.beginningOfDocument, to: nameTextField.endOfDocument)
}
}

if let fileName = (textField as? UITextField)?.text, let requiredFileExtension = row.requiredFileExtension {
saveBarButtonItem?.isEnabled = (fileName.count > (requiredFileExtension.count + 1))
}
}, placeholder: "Name".localized, value: fileName ?? "", keyboardType: .default, autocorrectionType: .no, enablesReturnKeyAutomatically: true, returnKeyType: .default, identifier: "name", accessibilityLabel: "Name".localized)
saveSection.add(row: fileNameRow!)
self.addSection(saveSection)
Expand Down
20 changes: 16 additions & 4 deletions ownCloudAppShared/Client/User Interface/NamingViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,16 @@
import UIKit
import ownCloudSDK

public typealias StringValidatorResult = (Bool, String?, String?)
public typealias StringValidatorHandler = (String) -> StringValidatorResult
public typealias StringValidatorResult = (Bool, String?, String?) // (validationPassed, validationErrorTitle, validationErrorMessage)
public typealias StringValidatorHandler = (_ stringToCheck: String) -> StringValidatorResult

open class NamingViewController: UIViewController {
weak open var item: OCItem?
weak open var core: OCCore?
open var completion: (String?, NamingViewController) -> Void
open var stringValidator: StringValidatorHandler?
open var defaultName: String?
open var requiredFileExtension: String?

private var blurView: UIVisualEffectView

Expand All @@ -37,7 +38,7 @@ open class NamingViewController: UIViewController {
private var thumbnailImageView: ResourceViewHost

private var nameContainer: UIView
private var nameTextField: UITextField
private var nameTextField: ThemeCSSTextField

private var textfieldTopAnchorConstraint: NSLayoutConstraint
private var textfieldCenterYAnchorConstraint: NSLayoutConstraint
Expand Down Expand Up @@ -152,6 +153,7 @@ open class NamingViewController: UIViewController {

// Name textfield
nameTextField.translatesAutoresizingMaskIntoConstraints = false
nameTextField.requiredFileExtension = requiredFileExtension
nameContainer.addSubview(nameTextField)
NSLayoutConstraint.activate([
nameTextField.heightAnchor.constraint(equalToConstant: 40),
Expand Down Expand Up @@ -267,7 +269,9 @@ open class NamingViewController: UIViewController {
}

@objc open func textfieldDidChange(_ sender: UITextField) {
if sender.text != "" {
let filename = sender.text

if filename != "", requiredFileExtension == nil || ((requiredFileExtension != nil) && filename != ".\(requiredFileExtension!)") {
doneButton?.isEnabled = true
} else {
doneButton?.isEnabled = false
Expand Down Expand Up @@ -351,7 +355,15 @@ extension NamingViewController: UITextFieldDelegate {
} else {
textField.selectedTextRange = nameTextField.textRange(from: nameTextField.beginningOfDocument, to: nameTextField.endOfDocument)
}
}

public func textFieldShouldClear(_ textField: UITextField) -> Bool {
if let requiredFileExtension {
textField.text = "." + requiredFileExtension
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.beginningOfDocument)
return false
}
return true
}

open func textFieldShouldReturn(_ textField: UITextField) -> Bool {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,21 @@ open class StaticTableViewRow : NSObject, UITextFieldDelegate {
return true
}

public func textFieldShouldClear(_ textField: UITextField) -> Bool {
if let requiredFileExtension {
textField.text = "." + requiredFileExtension
textField.selectedTextRange = textField.textRange(from: textField.beginningOfDocument, to: textField.beginningOfDocument)
return false
}
return true
}

open var requiredFileExtension: String? {
didSet {
(textField as? ThemeCSSTextField)?.requiredFileExtension = requiredFileExtension
}
}

// MARK: - Labels
convenience public init(label: String, accessoryView: UIView? = nil, identifier: String? = nil) {
self.init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,20 @@

import UIKit

public extension UITextInput {
func range(from textRange: UITextRange) -> NSRange {
let startOffset = offset(from: beginningOfDocument, to: textRange.start)
let endOffset = offset(from: beginningOfDocument, to: textRange.end)

return NSRange(location: startOffset, length: endOffset-startOffset+1)
}
}

public class ThemeCSSTextField: UITextField, Themeable {
private var hasRegistered : Bool = false

open var requiredFileExtension: String?

override open func didMoveToWindow() {
super.didMoveToWindow()

Expand All @@ -41,4 +52,26 @@ public class ThemeCSSTextField: UITextField, Themeable {
open func applyThemeCollection(theme: Theme, collection: ThemeCollection, event: ThemeEvent) {
self.applyThemeCollection(collection)
}

public override func shouldChangeText(in range: UITextRange, replacementText: String) -> Bool {
if let requiredFileExtension, let previousText = text,
let textRange = Range(self.range(from: range), in: previousText) {
let newName = previousText.replacingCharacters(in: textRange, with: replacementText)
return (newName as NSString).pathExtension == requiredFileExtension || (newName == ".\(requiredFileExtension)")
}

return super.shouldChangeText(in: range, replacementText: replacementText)
}

public override var selectedTextRange: UITextRange? {
didSet {
if let requiredFileExtension {
if let selectedTextRange,
let maxAllowedPosition = position(from: endOfDocument, offset: -requiredFileExtension.count-1),
compare(selectedTextRange.end, to: maxAllowedPosition) == .orderedDescending {
self.selectedTextRange = textRange(from: selectedTextRange.start, to: maxAllowedPosition)
}
}
}
}
}