Warning, /network/kdenetwork-filesharing/samba/filepropertiesplugin/qml/ACLPage.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003     SPDX-FileCopyrightText: 2020 Harald Sitter <sitter@kde.org>
0004     SPDX-FileCopyrightText: 2021 Slava Aseev <nullptrnine@basealt.ru>
0005     SPDX-FileCopyrightText: 2023 ivan tkachenko <me@ratijas.tk>
0006 */
0007 
0008 import QtQuick 2.12
0009 import QtQuick.Controls 2.5 as QQC2
0010 import QtQuick.Layouts 1.14
0011 import org.kde.kirigami 2.4 as Kirigami
0012 import org.kde.filesharing.samba 1.0 as Samba
0013 
0014 // NOTE: sambaPlugin.shareContext is a singleton its properties cannot be bound and need manual syncing back.
0015 
0016 Item {
0017     // NOTE: we cannot use a Kirigami.Page for this because it adds excessive padding that we can't disable
0018     //   so it'd very awkwardly space within the properties dialog reducing the available space a lot and looking
0019     //   silly. Alas, Column is also not grand because it gets collapsed by the sheet, so we use a fixed size
0020     //   Item in place of a Page that gets covered by the Sheet and then that Item is filled by a Column.
0021     id: page
0022 
0023     Kirigami.OverlaySheet {
0024         id: denialSheet
0025         property bool shownOnce: false // maybeShow() this sheet only once per run not annoy users too much
0026         parent: page // there's a bug where the sheet doesn't manage to find its parent, explicitly set it
0027 
0028         header: Kirigami.Heading {
0029             text: i18nc("@title", "Denying Access")
0030         }
0031 
0032         function maybeOpen() {
0033             if (shownOnce) {
0034                 return
0035             }
0036             shownOnce = true
0037             open()
0038         }
0039 
0040         QQC2.Label {
0041             Layout.fillWidth: true
0042             textFormat: Text.RichText // for xi18n markup; this also means newlines are ignored!
0043             text: xi18nc("@info", `
0044 Denying access prevents using this share even when another access rule might grant access. A denial rule always
0045 takes precedence. In particular denying access to <resource>Everyone</resource> actually disables access for everyone.
0046 It is generally not necessary to deny anyone because the regular directory and file permissions still apply to shared
0047 directories. A user who does not have access to the directory locally will still not be able to access it remotely even
0048 when the Share access rules would allow it.`)
0049             wrapMode: Text.Wrap
0050         }
0051     }
0052 
0053     ColumnLayout {
0054         anchors.fill: parent
0055 
0056         Kirigami.InlineMessage {
0057             id: changePermissionsWarning
0058             Layout.fillWidth: true
0059             showCloseButton: true
0060             visible: sambaPlugin.permissionsHelper.permissionsChangeRequired
0061             type: Kirigami.MessageType.Warning
0062             text: i18nc("@label", "This folder needs extra permissions for sharing to work")
0063 
0064             actions: [
0065                 Kirigami.Action {
0066                     text: i18nc("@action:button opens the change permissions page", "Fix Permissions")
0067                     onTriggered: stack.push("ChangePermissionsPage.qml")
0068                 }
0069             ]
0070         }
0071 
0072         Kirigami.InlineMessage {
0073             id: posixACLWarning
0074             Layout.fillWidth: true
0075             showCloseButton: true
0076             visible: sambaPlugin.permissionsHelper.hasPosixACL
0077             type: Kirigami.MessageType.Warning
0078             text: xi18nc("@label", "The share might not work properly because share folder or its paths has Advanced Permissions: %1",
0079                             sambaPlugin.permissionsHelper.pathsWithPosixACL.join(", "))
0080         }
0081 
0082         QQC2.CheckBox {
0083             id: shareEnabled
0084             Layout.fillWidth: true
0085             text: i18nc("@option:check", "Share this folder with other computers on the local network")
0086             checked: sambaPlugin.shareContext.enabled
0087             onToggled: {
0088                 sambaPlugin.shareContext.enabled = checked
0089                 sambaPlugin.dirty = true
0090             }
0091         }
0092 
0093         ColumnLayout {
0094             Layout.fillWidth: true
0095             Layout.fillHeight: true
0096             enabled: shareEnabled.checked
0097 
0098             RowLayout {
0099                 Layout.fillWidth: true
0100                 QQC2.Label {
0101                     Layout.maximumWidth: Math.round(page.width / 2.0) // Don't let the label use more than half the space, elide the rest.
0102                     text: i18nc("@label", "Name:")
0103                     elide: Text.ElideRight
0104                 }
0105                 QQC2.TextField {
0106                     id: nameField
0107                     Layout.fillWidth: true
0108                     text: sambaPlugin.shareContext.name
0109                     onTextEdited: {
0110                         if (text.length > sambaPlugin.shareContext.maximumNameLength) {
0111                             tooLongMessage.visible = true;
0112                             // This is a soft limit, do not return.
0113                         } else {
0114                             tooLongMessage.visible = false
0115                         }
0116 
0117                         if (!sambaPlugin.shareContext.isNameFree(text)) {
0118                             alreadyUsedError.visible = true;
0119                             return
0120                         }
0121                         alreadyUsedError.visible = false;
0122 
0123                         sambaPlugin.shareContext.name = text
0124                         sambaPlugin.dirty = true
0125                     }
0126                 }
0127             }
0128 
0129             Kirigami.InlineMessage {
0130                 id: alreadyUsedError
0131                 Layout.fillWidth: true
0132                 type: Kirigami.MessageType.Error
0133                 text: i18nc("@label",
0134                             "This name cannot be used. Share names must not be user names and there must not be two shares with the same name on the entire system.")
0135                 visible: false
0136             }
0137 
0138             Kirigami.InlineMessage {
0139                 id: tooLongMessage
0140                 Layout.fillWidth: true
0141                 type: Kirigami.MessageType.Warning
0142                 text: i18nc("@label",
0143                             "This name may be too long. It can cause interoperability problems or get rejected by Samba.")
0144                 visible: false
0145             }
0146 
0147             QQC2.CheckBox {
0148                 id: allowGuestBox
0149                 Layout.fillWidth: true
0150                 text: i18nc("@option:check", "Allow guests")
0151                 enabled: sambaPlugin.shareContext.canEnableGuest
0152                 checked: sambaPlugin.shareContext.guestEnabled
0153                 onToggled: {
0154                     sambaPlugin.shareContext.guestEnabled = checked
0155                     sambaPlugin.dirty = true
0156                 }
0157             }
0158 
0159             QQC2.Label {
0160                 Layout.fillWidth: true
0161                 enabled: false // looks more visually connected if both are disabled
0162                 visible: !allowGuestBox.enabled
0163                 text: i18nc("@label", "Guest access is disabled by the system's Samba configuration.")
0164                 wrapMode: Text.Wrap
0165                 font: Kirigami.Theme.smallFont
0166             }
0167 
0168             // TODO: this could benefit form some splitting. This is half the file.
0169             QQC2.ScrollView {
0170                 id: scroll
0171 
0172                 Layout.fillHeight: true
0173                 Layout.fillWidth: true
0174 
0175                 activeFocusOnTab: false
0176                 Kirigami.Theme.colorSet: Kirigami.Theme.View
0177                 Kirigami.Theme.inherit: false
0178 
0179                 Component.onCompleted: {
0180                     if (background) {
0181                         background.visible = true
0182                     }
0183                 }
0184 
0185                 TableView {
0186                     id: view
0187 
0188                     property bool itemComplete: false
0189 
0190                     model: sambaPlugin.userPermissionModel
0191 
0192                     topMargin: Kirigami.Units.smallSpacing
0193                     // This wouldn't work with horizontal scrollbar, but here
0194                     // it doesn't matter, because it is never visible.
0195                     bottomMargin: Kirigami.Units.smallSpacing
0196 
0197                     rowSpacing: Kirigami.Units.smallSpacing
0198                     columnSpacing: Kirigami.Units.smallSpacing
0199 
0200                     columnWidthProvider: column => {
0201                         // Give 2/3 of the width to the access column for better looks.
0202                         const availableWidth = width - columnSpacing
0203                         var accessWidth = Math.round(availableWidth / 1.5)
0204                         if (column === Samba.UserPermissionModel.ColumnAccess) {
0205                             return accessWidth
0206                         }
0207                         return availableWidth - accessWidth
0208                     }
0209 
0210                     Timer {
0211                         // Helper timer to delay force layouting through the event loop.
0212                         // We want width changes to recalculate the column widths so we need to force a layout run,
0213                         // when doing that directly in onWidthChanged that has a chance to produce errors so instead
0214                         // we'll queue a timeout for the next event loop to force the layout run.
0215                         //   TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!
0216                         id: forceLayoutTimer
0217                         interval: 0
0218                         running: false
0219                         repeat: false
0220                         onTriggered: {
0221                             // forceLayout docs say it must only be called after component completion, so make sure of that.
0222                             if (view.itemComplete) {
0223                                 view.forceLayout()
0224                             }
0225                         }
0226                     }
0227 
0228                     onWidthChanged: forceLayoutTimer.start() // make sure columns get recalculated
0229 
0230                     delegate: RowLayout {
0231                         // This is only a layout to conveniently forward the child size regardless of which child is in
0232                         // use.
0233                         Layout.fillWidth: true
0234                         TableView.onReused: combo.loadCurrentIndex()
0235 
0236                         QQC2.Label {
0237                             Layout.fillWidth: true
0238                             Layout.leftMargin: view.columnSpacing
0239                             visible: column !== Samba.UserPermissionModel.ColumnAccess
0240                             text: display === undefined ? "" : display
0241                             elide: Text.ElideMiddle
0242                         }
0243 
0244                         QQC2.ComboBox {
0245                             id: combo
0246                             Layout.fillWidth: true
0247                             Layout.rightMargin: view.columnSpacing
0248                             textRole: "text"
0249                             valueRole: "value"
0250                             visible: column === Samba.UserPermissionModel.ColumnAccess
0251                             model: [
0252                                 { value: undefined, text: "---" },
0253                                 { value: "F", text: i18nc("@option:radio user can read&write", "Full Control") },
0254                                 { value: "R", text: i18nc("@option:radio user can read", "Read Only") },
0255                                 { value: "D", text: i18nc("@option:radio user not allowed to access share", "No Access") }
0256                             ]
0257                             onActivated: {
0258                                 edit = currentValue // setData on model with edit role
0259                                 if (currentValue === 'D') {
0260                                     denialSheet.maybeOpen()
0261                                 }
0262                                 sambaPlugin.dirty = true
0263                                 sambaPlugin.permissionsHelper.reload()
0264                             }
0265 
0266                             function loadCurrentIndex() {
0267                                 currentIndex = indexOfValue(edit)
0268                             }
0269 
0270                             Component.onCompleted: loadCurrentIndex()
0271                         }
0272                     }
0273 
0274                     Component.onCompleted: itemComplete = true
0275                 }
0276             }
0277         }
0278 
0279         QQC2.Button {
0280             Layout.fillWidth: true
0281             text: i18nc("@button", "Show Samba status monitor")
0282             onClicked: sambaPlugin.showSambaStatus()
0283         }
0284     }
0285 }