Commit 6e7878af authored by Harald Sitter's avatar Harald Sitter 🌈
Browse files

rejigger acl page and add a sheet to be shown when using a denial

denials always win so they shouldn't really be used unless one is
suuuuuuuuuuuuper sure about it.
to that end we'll throw up a warning that tries to explain that denials
probably aren't what the user wants and also that denying usually isn't
necessary because filesystem permissions still apply, so even when using
a liberal share ACL the filesystem ACL would still prevent other users
from reading one's private data for example

this was a bit awkward to implement because sheets need to overlay
something yet overlaying the columnlayout messes with the layout size
itself compacting the tableview away.
equally we cannot use a regular page because pages come with a whole
bunch of padding that looks very weird here and I've not found a way to
disable it either. instead we now have a new toplevel item which merely
acts as the thing the sheet can overlay. the item then contains the
previous layout and elements so they retain the sizing even when
overlayed

BUG: 422554
FIXED-IN: 20.12
parent f3c12f12
......@@ -11,175 +11,216 @@ import org.kde.filesharing.samba 1.0 as Samba
// NOTE: Samba.ShareContext is a singleton its properties cannot be bound and need manual syncing back.
ColumnLayout {
Item {
// NOTE: we cannot use a Kirigami.Page for this because it adds excessive padding that we can't disable
// so it'd very awkwardly space within the properties dialog reducing the available space a lot and looking
// silly. Alas, Column is also not grand because it gets collapsed by the sheet, so we use a fixed size
// Item in place of a Page that gets covered by the Sheet and then that Item is filled by a Column.
id: page
QQC2.CheckBox {
id: shareEnabled
text: i18nc("@option:check", "Share this folder with other computers on the local network")
checked: Samba.ShareContext.enabled
onToggled: {
Samba.ShareContext.enabled = checked
Samba.Plugin.dirty = true
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
enabled: shareEnabled.checked
RowLayout {
Layout.fillWidth: true
QQC2.Label {
Layout.maximumWidth: Math.round(page.width / 2.0) // Don't let the label use more than half the space, elide the rest.
text: i18nc("@label", "Name:")
elide: Text.ElideRight
}
QQC2.TextField {
id: nameField
Layout.fillWidth: true
text: Samba.ShareContext.name
onTextEdited: {
if (text.length > Samba.ShareContext.maximumNameLength) {
tooLongMessage.visible = true;
// This is a soft limit, do not return.
} else {
tooLongMessage.visible = false
}
Kirigami.OverlaySheet {
id: denialSheet
property bool shownOnce: false // maybeShow() this sheet only once per run not annoy users too much
parent: page // there's a bug where the sheet doesn't manage to find its parent, explicitly set it
if (!Samba.ShareContext.isNameFree(text)) {
alreadyUsedError.visible = true;
return
}
alreadyUsedError.visible = false;
header: Kirigami.Heading {
text: i18nc("@title", "Denying Access")
}
Samba.ShareContext.name = text
Samba.Plugin.dirty = true
}
function maybeOpen() {
if (shownOnce) {
return
}
shownOnce = true
open()
}
Kirigami.InlineMessage {
id: alreadyUsedError
QQC2.Label {
Layout.fillWidth: true
type: Kirigami.MessageType.Error
text: i18nc("@label",
"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.")
visible: false
textFormat: Text.RichText // for xi18n markup; this also means newlines are ignored!
text: xi18nc("@info", `
Denying access prevents using this share even when another access rule might grant access. A denial rule always
takes precedence. In particular denying access to <resource>Everyone</resource> actually disables access for everyone.
It is generally not necessary to deny anyone because the regular directory and file permissions still apply to shared
directories. A user who does not have access to the directory locally will still not be able to access it remotely even
when the Share access rules would allow it.`)
wrapMode: Text.Wrap
}
}
Kirigami.InlineMessage {
id: tooLongMessage
Layout.fillWidth: true
type: Kirigami.MessageType.Warning
text: i18nc("@label",
"This name may be too long. It can cause interoperability problems or get rejected by Samba.")
visible: false
}
ColumnLayout {
anchors.fill: parent
QQC2.CheckBox {
text: i18nc("@option:check", "Allow guests")
checked: Samba.ShareContext.guestEnabled
id: shareEnabled
text: i18nc("@option:check", "Share this folder with other computers on the local network")
checked: Samba.ShareContext.enabled
onToggled: {
Samba.ShareContext.guestEnabled = checked
Samba.ShareContext.enabled = checked
Samba.Plugin.dirty = true
}
}
// TODO: this could benefit form some splitting. This is half the file.
QQC2.ScrollView {
id: scroll
Layout.fillHeight: true
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
enabled: shareEnabled.checked
activeFocusOnTab: false
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
RowLayout {
Layout.fillWidth: true
QQC2.Label {
Layout.maximumWidth: Math.round(page.width / 2.0) // Don't let the label use more than half the space, elide the rest.
text: i18nc("@label", "Name:")
elide: Text.ElideRight
}
QQC2.TextField {
id: nameField
Layout.fillWidth: true
text: Samba.ShareContext.name
onTextEdited: {
if (text.length > Samba.ShareContext.maximumNameLength) {
tooLongMessage.visible = true;
// This is a soft limit, do not return.
} else {
tooLongMessage.visible = false
}
Component.onCompleted: background.visible = true // crashes when initialized with this. god knows why
if (!Samba.ShareContext.isNameFree(text)) {
alreadyUsedError.visible = true;
return
}
alreadyUsedError.visible = false;
contentItem: TableView {
id: view
Samba.ShareContext.name = text
Samba.Plugin.dirty = true
}
}
}
property bool itemComplete: false
Kirigami.InlineMessage {
id: alreadyUsedError
Layout.fillWidth: true
type: Kirigami.MessageType.Error
text: i18nc("@label",
"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.")
visible: false
}
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
clip: true
interactive: false
model: Samba.UserPermissionModel
Kirigami.InlineMessage {
id: tooLongMessage
Layout.fillWidth: true
type: Kirigami.MessageType.Warning
text: i18nc("@label",
"This name may be too long. It can cause interoperability problems or get rejected by Samba.")
visible: false
}
columnWidthProvider: function (column) {
// Give 2/3 of the width to the access column for better looks.
var accessWidth = Math.round(width / 1.5)
if (column == Samba.UserPermissionModel.ColumnAccess) {
return accessWidth
}
return width - accessWidth
QQC2.CheckBox {
text: i18nc("@option:check", "Allow guests")
checked: Samba.ShareContext.guestEnabled
onToggled: {
Samba.ShareContext.guestEnabled = checked
Samba.Plugin.dirty = true
}
}
// TODO: this could benefit form some splitting. This is half the file.
QQC2.ScrollView {
id: scroll
Layout.fillHeight: true
Layout.fillWidth: true
activeFocusOnTab: false
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
Component.onCompleted: background.visible = true // crashes when initialized with this. god knows why
contentItem: TableView {
id: view
property bool itemComplete: false
Timer {
// Helper timer to delay force layouting through the event loop.
// We want width changes to recalculate the column widths so we need to force a layout run,
// when doing that directly in onWidthChanged that has a chance to produce errors so instead
// we'll queue a timeout for the next event loop to force the layout run.
// TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!
id: forceLayoutTimer
interval: 0
running: false
repeat: false
onTriggered: {
// forceLayout docs say it must only be called after component completion, so make sure of that.
if (view.itemComplete) {
view.forceLayout()
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
clip: true
interactive: false
model: Samba.UserPermissionModel
columnWidthProvider: function (column) {
// Give 2/3 of the width to the access column for better looks.
var accessWidth = Math.round(width / 1.5)
if (column == Samba.UserPermissionModel.ColumnAccess) {
return accessWidth
}
return width - accessWidth
}
}
onWidthChanged: forceLayoutTimer.start() // make sure columns get recalculated
Timer {
// Helper timer to delay force layouting through the event loop.
// We want width changes to recalculate the column widths so we need to force a layout run,
// when doing that directly in onWidthChanged that has a chance to produce errors so instead
// we'll queue a timeout for the next event loop to force the layout run.
// TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!
id: forceLayoutTimer
interval: 0
running: false
repeat: false
onTriggered: {
// forceLayout docs say it must only be called after component completion, so make sure of that.
if (view.itemComplete) {
view.forceLayout()
}
}
}
delegate: ColumnLayout {
// This is only a layout to conveniently forward the child size regardless of which child is in
// use.
Layout.fillWidth: true
onWidthChanged: forceLayoutTimer.start() // make sure columns get recalculated
QQC2.Label {
delegate: RowLayout {
// This is only a layout to conveniently forward the child size regardless of which child is in
// use.
Layout.fillWidth: true
visible: !combo.visible
text: display === undefined ? "" : display
elide: Text.ElideMiddle
}
QQC2.ComboBox {
Layout.fillWidth: true
id: combo
textRole: "text"
valueRole: "value"
visible: column == Samba.UserPermissionModel.ColumnAccess
model: [
{ value: undefined, text: "---" },
{ value: "F", text: i18nc("@option:radio user can read&write", "Full Control") },
{ value: "R", text: i18nc("@option:radio user can read", "Read Only") },
{ value: "D", text: i18nc("@option:radio user not allowed to access share", "No Access") }
]
onActivated: {
edit = currentValue // setData on model with edit role
Samba.Plugin.dirty = true
QQC2.Label {
Layout.fillWidth: true
visible: !combo.visible
text: display === undefined ? "" : display
elide: Text.ElideMiddle
}
QQC2.ComboBox {
id: combo
Layout.fillWidth: true
textRole: "text"
valueRole: "value"
visible: column == Samba.UserPermissionModel.ColumnAccess
model: [
{ value: undefined, text: "---" },
{ value: "F", text: i18nc("@option:radio user can read&write", "Full Control") },
{ value: "R", text: i18nc("@option:radio user can read", "Read Only") },
{ value: "D", text: i18nc("@option:radio user not allowed to access share", "No Access") }
]
onActivated: {
edit = currentValue // setData on model with edit role
if (currentValue === 'D') {
denialSheet.maybeOpen()
}
Samba.Plugin.dirty = true
}
Component.onCompleted: currentIndex = indexOfValue(edit)
}
Component.onCompleted: currentIndex = indexOfValue(edit)
}
}
Component.onCompleted: itemComplete = true
Component.onCompleted: itemComplete = true
}
}
}
}
QQC2.Button {
Layout.fillWidth: true
text: i18nc("@button", "Show Samba status monitor")
onClicked: Samba.Plugin.showSambaStatus()
QQC2.Button {
Layout.fillWidth: true
text: i18nc("@button", "Show Samba status monitor")
onClicked: Samba.Plugin.showSambaStatus()
}
}
}
\ No newline at end of file
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment