Samba: UI: Show toggle buttons and share names

Closes #1989

I checked that all Samba tests pass.

Signed-off-by: Veiko Aasa <veiko17@disroot.org>
[sunil: Minor indentation and styling fixes in template]
[sunil: Prevent multiple HTML attributes with same name in case of vfat]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
Veiko Aasa 2020-11-30 10:15:32 +02:00 committed by Sunil Mohan Adapa
parent 89715c51de
commit fcc4a933d2
No known key found for this signature in database
GPG Key ID: 43EA1CFF0AA7C5F2
4 changed files with 136 additions and 96 deletions

View File

@ -5,7 +5,6 @@ FreedomBox app to configure samba.
import grp
import json
import os
import pwd
import socket
@ -151,15 +150,6 @@ def get_shares():
return json.loads(output)
def disk_name(mount_point):
"""Get a disk name."""
share_name = os.path.basename(mount_point)
if not share_name:
share_name = 'disk'
return share_name
def backup_pre(packet):
"""Save registry share configuration."""
actions.superuser_run('samba', ['dump-shares'])

View File

@ -12,11 +12,19 @@
<style type="text/css">
.progress {
margin-bottom: 0;
width: 180px;
}
.samba-disk-name {
font-size: 1.3em;
float: left;
}
.samba-disk-shares {
padding-top: 10px;
margin-bottom: 20px;
}
</style>
{% endblock %}
{% block configuration %}
{{ block.super }}
@ -28,84 +36,112 @@
not the whole disk.
{% endblocktrans %}
</p>
<table class="table table-bordered table-condensed table-striped">
<thead>
<tr>
<th>{% trans "Disk Name" %}</th>
<th>{% trans "Shares" %}</th>
<th>{% trans "Used" %}</th>
</tr>
</thead>
<tbody>
{% for disk in disks %}
<tr>
<td>{{ disk.name|default_if_none:"" }}</td>
<td>
<form class="form shareform" method="post"
action="{% url 'samba:share' disk.mount_point|urlencode:'' %}">
{% csrf_token %}
<input type="hidden" name="filesystem_type" value="{{ disk.filesystem_type }}">
{% for share_type in share_types %}
<button type="submit"
{% if share_type.0 in shared_mounts|lookup:disk.mount_point %}
class="btn btn-success" name="{{ share_type.0 }}_share" value="disable"
{% else %}
class="btn btn-default" name="{{ share_type.0 }}_share" value="enable"
{% endif %}
{% if disk.filesystem_type == 'vfat' %}
title='{% trans "VFAT partitions are not supported" %}' disabled
{% endif %}>{{ share_type.1 }}
</button>
{% endfor %}
</form>
</td>
<td >
{% for disk in disks %}
<div class="samba-disk-shares">
<div class="samba-disk-header">
<div class="samba-disk-name">
<span class="fa fa-hdd-o"></span>
{{ disk.name }}
</div>
<div class="pull-right">
<div class="progress">
{% if disk.percent_used < 75 %}
<div class="progress-bar progress-bar-striped progress-bar-success"
{% elif disk.percent_used < 90 %}
<div class="progress-bar progress-bar-striped progress-bar-warning"
{% else %}
<div class="progress-bar progress-bar-striped progress-bar-danger"
{% endif %}
role="progressbar" aria-valuenow="{{ disk.percent_used }}"
aria-valuemin="0" aria-valuemax="100"
style="width: {{ disk.percent_used }}%;">
{{ disk.percent_used }}%
</div>
<div class="progress-bar progress-bar-striped
{% if disk.percent_used < 75 %}
progress-bar-success
{% elif disk.percent_used < 90 %}
progress-bar-warning
{% else %}
progress-bar-danger
{% endif %}"
role="progressbar" aria-valuenow="{{ disk.percent_used }}"
aria-valuemin="0" aria-valuemax="100"
style="width: {{ disk.percent_used }}%;">
{{ disk.percent_used }}%
</div>
</div>
<div>{{ disk.used_str }} / {{ disk.size_str }}</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<p>
<form class="form shareform" method="post"
action="{% url 'samba:share' disk.mount_point|urlencode:'' %}">
{% csrf_token %}
<input type="hidden" name="filesystem_type"
value="{{ disk.filesystem_type }}">
<table class="table table-condensed"
aria-describedby="{{ disk.name }}">
<thead>
<tr>
<th scope="col">{% trans 'Type' %}</th>
<th scope="col">{% trans 'Name' %}</th>
<th scope="col">{% trans 'Status' %}</th>
</tr>
</thead>
<tbody>
{% for share_type in share_types %}
<tr id="samba-share-{{ disk.share_name_prefix }}-{{ share_type.id }}"
class="share">
<td class="share-type">
{{ share_type.type }}
</td>
<td class="share-name">
{{ disk.share_name_prefix }}{{ share_type.share_name_suffix }}
</td>
<td class="share-status">
<button type="submit"
{% if disk.filesystem_type == 'vfat' %}
class="btn toggle-button"
title='{% trans "VFAT partitions are not supported" %}'
disabled
aria-readonly="true"
{% elif share_type.id in shared_mounts|lookup:disk.mount_point %}
class="btn toggle-button toggle-button--toggled"
name="{{ share_type.id }}_share"
value="disable"
aria-pressed="true"
{% else %}
class="btn toggle-button"
name="{{ share_type.id }}_share"
value="enable"
aria-pressed="false"
{% endif %}>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div>
{% endfor %}
<p>
{% url 'storage:index' as storage_url %}
{% url 'users:index' as users_url %}
{% blocktrans trimmed %}
You can find additional information about disks on the
<a href="{{ storage_url }}">storage</a> module page and configure
access to the shares on the <a href="{{ users_url }}">users</a> module page.
{% endblocktrans %}</p>
access to the shares on the <a href="{{ users_url }}">users</a> module
page.
{% endblocktrans %}
</p>
<p>{% trans "Users who can currently access group and home shares" %}:
{{ users.access_ok|join:", " }}</p>
<p>{% trans "Users who can currently access group and home shares" %}:
{{ users.access_ok|join:", " }}</p>
{% if users.password_re_enter_needed %}
<p>{% trans "Users needing to re-enter their password on the password change page to access group and home shares" %}:
<strong>{{ users.password_re_enter_needed|join:", " }}</strong>.</p>
{% endif %}
{% if users.password_re_enter_needed %}
<p>{% trans "Users needing to re-enter their password on the password change page to access group and home shares" %}:
<strong>{{ users.password_re_enter_needed|join:", " }}</strong>.</p>
{% endif %}
{% if unavailable_shares %}
<h3>{% trans "Unavailable Shares" %}</h3>
<p>
{% blocktrans trimmed %}
Shares that are configured but the disk is not available. If the disk
is plugged back in, sharing will be automatically enabled.
{% endblocktrans %}
{% blocktrans trimmed %}
Shares that are configured but the disk is not available. If the disk
is plugged back in, sharing will be automatically enabled.
{% endblocktrans %}
</p>
<table class="table table-bordered table-condensed table-striped">
<thead>

View File

@ -48,17 +48,16 @@ def samba_share_should_not_be_available(share_type):
def _set_share(browser, share_type, status='enabled'):
"""Enable or disable samba share."""
disk_name = 'disk'
share_type_name = '{0}_share'.format(share_type)
share_row_id = 'samba-share-{0}-{1}'.format(disk_name, share_type)
functional.nav_to_module(browser, 'samba')
for elem in browser.find_by_tag('td'):
if elem.text == disk_name:
share_form = elem.find_by_xpath('(..//*)[2]/form').first
share_btn = share_form.find_by_name(share_type_name).first
if status == 'enabled' and share_btn['value'] == 'enable':
share_btn.click()
elif status == 'disabled' and share_btn['value'] == 'disable':
share_btn.click()
break
share = browser.find_by_id(share_row_id)
share_btn = share.find_by_css('.share-status').find_by_tag('button').first
if status == 'enabled' and share_btn['value'] == 'enable':
share_btn.click()
elif status == 'disabled' and share_btn['value'] == 'disable':
share_btn.click()
def _write_to_share(share_type, as_guest=False):

View File

@ -4,6 +4,7 @@ Views for samba module.
"""
import logging
import os
import urllib.parse
from collections import defaultdict
@ -20,12 +21,19 @@ logger = logging.getLogger(__name__)
def get_share_mounts():
"""Return list of mount points."""
ignore_points = ('/boot', '/boot/efi', '/boot/firmware', '/.snapshots')
return [
mount for mount in storage.get_mounts()
if mount['mount_point'] not in ignore_points
]
"""Return list of shareable mounts."""
ignore_mounts = ('/boot', '/boot/efi', '/boot/firmware', '/.snapshots')
mounts = []
for mount in storage.get_mounts():
mount_point = mount['mount_point']
if mount_point not in ignore_mounts:
basename = os.path.basename(mount_point)
mount['name'] = basename or _('FreedomBox OS disk')
mount['share_name_prefix'] = basename or 'disk'
mounts.append(mount)
return sorted(mounts, key=lambda k: k['mount_point'])
class SambaAppView(views.AppView):
@ -37,20 +45,27 @@ class SambaAppView(views.AppView):
"""Return template context data."""
context = super().get_context_data(*args, **kwargs)
disks = get_share_mounts()
shares = samba.get_shares()
for disk in disks:
disk['name'] = samba.disk_name(disk['mount_point'])
context['disks'] = disks
shares = samba.get_shares()
shared_mounts = defaultdict(list)
for share in shares:
shared_mounts[share['mount_point']].append(share['share_type'])
context['shared_mounts'] = shared_mounts
context['share_types'] = [('open', _('Open Share')),
('group', _('Group Share')),
('home', _('Home Share'))]
context['share_types'] = [{
'id': 'open',
'type': _('Open Share'),
'share_name_suffix': ''
}, {
'id': 'group',
'type': _('Group Share'),
'share_name_suffix': '_group'
}, {
'id': 'home',
'type': _('Home Share'),
'share_name_suffix': '_home'
}]
unavailable_shares = []
for share in shares: