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

Terms of service #3234

Merged
merged 10 commits into from
Dec 11, 2024
Merged
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: 1 addition & 1 deletion Brand/NCBrand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ let userAgent: String = {
var textCopyrightNextcloudiOS: String = "Nextcloud Hydrogen for iOS %@ © 2024"
var textCopyrightNextcloudServer: String = "Nextcloud Server %@"
var loginBaseUrl: String = "https://cloud.nextcloud.com"
@objc var pushNotificationServerProxy: String = "https://push-notifications.nextcloud.com"
var pushNotificationServerProxy: String = "https://push-notifications.nextcloud.com"
var linkLoginHost: String = "https://nextcloud.com/install"
var linkloginPreferredProviders: String = "https://nextcloud.com/signup-ios"
var webLoginAutenticationProtocol: String = "nc://" // example "abc://"
Expand Down
20 changes: 18 additions & 2 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@
F711A4EB2AF9327D00095DD8 /* UIImage+animatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */; };
F711A4EF2AF932B900095DD8 /* SVGKitSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F711A4EE2AF932B900095DD8 /* SVGKitSwift */; };
F711D63128F44801003F43C8 /* IntentHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C9739428F17131002C43E2 /* IntentHandler.swift */; };
F7132C722D085AD200B42D6A /* NCTermOfServiceModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7132C6B2D085AD200B42D6A /* NCTermOfServiceModel.swift */; };
F7132C732D085AD200B42D6A /* NCTermOfServiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7132C6C2D085AD200B42D6A /* NCTermOfServiceView.swift */; };
F713FBE52C31645200F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
F713FBE62C31646400F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
F713FBE72C31646500F10760 /* NCNetworking+AsyncAwait.swift in Sources */ = {isa = PBXBuildFile; fileRef = F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */; };
Expand Down Expand Up @@ -1258,6 +1260,8 @@
F710D1F42405770F00A6033D /* NCViewerPDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerPDF.swift; sourceTree = "<group>"; };
F710D2012405826100A6033D /* NCViewer+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCViewer+Menu.swift"; sourceTree = "<group>"; };
F711A4DB2AF92CAD00095DD8 /* NCUtility+Date.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCUtility+Date.swift"; sourceTree = "<group>"; };
F7132C6B2D085AD200B42D6A /* NCTermOfServiceModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTermOfServiceModel.swift; sourceTree = "<group>"; };
F7132C6C2D085AD200B42D6A /* NCTermOfServiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTermOfServiceView.swift; sourceTree = "<group>"; };
F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCNetworking+AsyncAwait.swift"; sourceTree = "<group>"; };
F713FEFE2472764000214AF6 /* UIImage+animatedGIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+animatedGIF.h"; sourceTree = "<group>"; };
F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+animatedGIF.m"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2095,6 +2099,15 @@
path = Color;
sourceTree = "<group>";
};
F7132C6D2D085AD200B42D6A /* Terms of service */ = {
isa = PBXGroup;
children = (
F7132C6B2D085AD200B42D6A /* NCTermOfServiceModel.swift */,
F7132C6C2D085AD200B42D6A /* NCTermOfServiceView.swift */,
);
path = "Terms of service";
sourceTree = "<group>";
};
F713418B2597513800768D21 /* PushNotification */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -3091,6 +3104,7 @@
F79A65C12191D8DC00FF6DCC /* Select */,
F728CE741BF6322C00E69702 /* Share */,
F7169A161EE590930086BD69 /* Shares */,
F7132C6D2D085AD200B42D6A /* Terms of service */,
F7E9C41320F4CA870040CF18 /* Transfers */,
F78F74322163753B00C2ADAD /* Trash */,
F7EFC0CB256BF89300461AAD /* UserStatus */,
Expand Down Expand Up @@ -4367,6 +4381,8 @@
F7CBC1252BAC8B0000EC1D55 /* NCSectionFirstHeaderEmptyData.swift in Sources */,
F7D4BF3D2CA2E8D800A5E746 /* TOPasscodeKeypadView.m in Sources */,
F7D4BF3E2CA2E8D800A5E746 /* TOPasscodeSettingsKeypadView.m in Sources */,
F7132C722D085AD200B42D6A /* NCTermOfServiceModel.swift in Sources */,
F7132C732D085AD200B42D6A /* NCTermOfServiceView.swift in Sources */,
F7D4BF3F2CA2E8D800A5E746 /* TOPasscodeFixedInputView.m in Sources */,
F7D4BF402CA2E8D800A5E746 /* TOPasscodeButtonLabel.m in Sources */,
F7D4BF412CA2E8D800A5E746 /* TOPasscodeViewControllerAnimatedTransitioning.m in Sources */,
Expand Down Expand Up @@ -5487,7 +5503,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = NKUJUXUJ3B;
ENABLE_STRICT_OBJC_MSGSEND = YES;
Expand Down Expand Up @@ -5553,7 +5569,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_TEAM = NKUJUXUJ3B;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
Expand Down
9 changes: 6 additions & 3 deletions iOSClient/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
}

func nextcloudPushNotificationAction(data: [String: AnyObject]) {
guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data),
let account = data["account"] as? String
guard let data = NCApplicationHandle().nextcloudPushNotificationAction(data: data)
else {
return
}
let account = data["account"] as? String ?? "unavailable"
let app = data["app"] as? String

func openNotification(controller: NCMainTabBarController) {
if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
if app == NCGlobal.shared.termsOfServiceName {
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterGetServerData, second: 0.5)
} else if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification {
viewController.session = NCSession.shared.getSession(account: account)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
let navigationController = UINavigationController(rootViewController: viewController)
Expand Down
14 changes: 14 additions & 0 deletions iOSClient/Files/NCFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import UIKit
import NextcloudKit
import RealmSwift
import SwiftUI

class NCFiles: NCCollectionViewCommon {
internal var isRoot: Bool = true
Expand Down Expand Up @@ -192,6 +193,19 @@ class NCFiles: NCCollectionViewCommon {
NCNetworking.shared.downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile))
}
}
} else if error.errorCode == self.global.errorForbidden {
DispatchQueue.main.async {
if self.presentedViewController == nil {
NextcloudKit.shared.getTermsOfService(account: self.session.account) { _, tos, _, error in
if error == .success, let tos {
let termOfServiceModel = NCTermOfServiceModel(controller: self.controller, tos: tos)
let termOfServiceView = NCTermOfServiceModelView(model: termOfServiceModel)
let termOfServiceController = UIHostingController(rootView: termOfServiceView)
self.present(termOfServiceController, animated: true, completion: nil)
}
}
}
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions iOSClient/NCGlobal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class NCGlobal: NSObject {
let talkName = "talk-message"
let spreedName = "spreed"
let twoFactorNotificatioName = "twofactor_nextcloud_notification"
let termsOfServiceName = "terms_of_service"

// Nextcloud version
//
Expand Down
3 changes: 3 additions & 0 deletions iOSClient/Supporting Files/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,9 @@
"_offline_not_allowed_" = "This operation is not allowed in offline mode";
"_Upload_native_format_yes_"= "Upload in native format: yes";
"_Upload_native_format_no_" = "Upload in native format: no";
"_terms_of_service_" = "Terms of service";
"_terms_accept_" = "I acknowledge that I have read and agree to the above terms of service";
"_terms_accepted_" = "Terms accepted";

// Tip
"_tip_pdf_thumbnails_" = "Swipe left from the right edge of the screen to show the thumbnails.";
Expand Down
70 changes: 70 additions & 0 deletions iOSClient/Terms of service/NCTermOfServiceModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2024 Marino Faggiana
// SPDX-License-Identifier: GPL-3.0-or-later

import Foundation
import NextcloudKit

/// A model that allows the user to configure the account
class NCTermOfServiceModel: ObservableObject {
/// Root View Controller
var controller: NCMainTabBarController?
/// Set true for dismiss the view
@Published var dismissView = false
// Data
@Published var languages: [String: String] = [:]
@Published var terms: [String: String] = [:]
@Published var termsId: [String: Int] = [:]
@Published var hasUserSigned: Bool = false

/// Initialization code
init(controller: NCMainTabBarController?, tos: NKTermsOfService?) {
self.controller = controller

if let terms = tos?.getTerms() {
for term in terms {
self.terms[term.languageCode] = term.body
self.termsId[term.languageCode] = term.id
}
} else {
languages = ["en": "English", "de": "Deutsch", "it": "Italiano"]
}

if let languages = tos?.getLanguages() {
for language in languages {
if self.terms[language.key] != nil {
self.languages[language.key] = language.value
}
}
} else {
terms = [
"en": "These are the Terms of Service.",
"de": "Dies sind die Allgemeinen Geschäftsbedingungen.",
"it": "Questi sono i Termini di servizio."
]
}

if let hasUserSigned = tos?.hasUserSigned() {
self.hasUserSigned = hasUserSigned
}
}

func signTermsOfService(termId: Int?) {
guard let termId,
let controller
else {
return
}

NextcloudKit.shared.signTermsOfService(termId: "\(termId)", account: controller.account) { _, _, error in
if error == .success {
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterGetServerData)
self.dismissView = true
} else {
NCContentPresenter().showError(error: error)
}
}
}

deinit { }
}
79 changes: 79 additions & 0 deletions iOSClient/Terms of service/NCTermOfServiceView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2024 Marino Faggiana
// SPDX-License-Identifier: GPL-3.0-or-later

import SwiftUI

struct NCTermOfServiceModelView: View {
@State private var selectedLanguage = Locale.preferredLanguages.first?.components(separatedBy: "-").first ?? "en"
@State private var termsText = "Loading terms..."
@ObservedObject var model: NCTermOfServiceModel

@Environment(\.presentationMode) var presentationMode

var body: some View {
VStack {
HStack {
Text(NSLocalizedString("_terms_of_service_", comment: "Terms of Service"))
.font(.headline)
.frame(maxWidth: .infinity, alignment: .leading)

Picker("Select Language", selection: $selectedLanguage) {
ForEach(model.languages.keys.sorted(), id: \.self) { key in
Text(model.languages[key] ?? "").tag(key)
}
}
.pickerStyle(MenuPickerStyle())
.frame(maxWidth: .infinity, alignment: .trailing)
.onChange(of: selectedLanguage) { newLanguage in
if let terms = model.terms[newLanguage] {
termsText = terms
} else {
selectedLanguage = model.languages.first?.key ?? "en"
termsText = model.terms[selectedLanguage] ?? "Terms not available in selected language."
}
}
}
.padding(.horizontal)

ScrollView {
Text(termsText)
.font(.body)
.foregroundColor(.primary)
.frame(maxWidth: .infinity)
.padding(.horizontal)
}
.padding(.top)

Button(action: {
model.signTermsOfService(termId: model.termsId[selectedLanguage])
}) {
Text(model.hasUserSigned ? NSLocalizedString("_terms_accepted_", comment: "Accepted terms") : NSLocalizedString("_terms_accept_", comment: "Accept terms"))
.foregroundColor(.white)
.padding()
.background(model.hasUserSigned ? Color.green : Color.blue)
.cornerRadius(10)
.padding(.bottom)
}
.disabled(model.hasUserSigned)
}
.padding()
.onAppear {
if let item = model.terms[selectedLanguage] {
termsText = item
} else {
selectedLanguage = model.languages.first?.key ?? "en"
termsText = model.terms[selectedLanguage] ?? "Terms not available in selected language."
}
}
.onReceive(model.$dismissView) { newValue in
if newValue {
presentationMode.wrappedValue.dismiss()
}
}
}
}

#Preview {
NCTermOfServiceModelView(model: NCTermOfServiceModel(controller: nil, tos: nil))
}
Loading