Sharing tables readonly with other users

- Added sharing feature for table owners to share their tables with
  other registered users.
- Fixed a bug where the wrong entries would be deleted or modified when
  searching or filtering.
This commit is contained in:
Johannes Randerath
2024-09-01 20:14:31 +02:00
parent c6cb3a48bc
commit 61708e5199
22 changed files with 766 additions and 167 deletions

View File

@@ -23,7 +23,7 @@
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">New table</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="$('#form_create_table).trigger('reset');"></button>
</div>
<div class="modal-body">
<form id="form_create_table" method="post" action="/table/create">
@@ -77,7 +77,7 @@
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Import new table</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="$('#form_import_table').trigger('reset');"></button>
</div>
<div class="modal-body">
<form action="/table/import" method="post" id="form_import_table">

View File

@@ -64,5 +64,51 @@
{% endfor %}
</div>
<hr>
{% if sharednms |length > 1 %}
<div class="accordion" id="shared_tables">
{% for tname in sharednms %}
{% set clms = sharedcols[loop.index0] %}
{% set rws = sharedrows[loop.index0] %}
{% set tid = sharedtblids[loop.index0] %}
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#acc_shared_{{ tid }}" aria-expanded="false" aria-controls="acc_shared_{{ tid }}">
{{ tname }}
</button>
</h2>
<div id="acc_shared_{{ tid }}" class="accordion-collapse collapse" data-bs-parent="#shared_tables">
<div class="accordion-body">
<div class="row justify-content-between">
<div class="col-auto"><h3>{{ tname }}</h3></div><div class="col-auto"><a class="btn btn-primary" href="/table/{{ tid }}">Open</a></div>
</div>
<!-- Preview -->
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
{% for clm in clms %}
<th>{{ clm }}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for row in rws %}
<tr>
<td>{{ loop.index }}</td>
{% for clm in clms %}
<td>{{ row[loop.index0] }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock body %}

View File

@@ -25,25 +25,30 @@
<!-- Table header and editing -->
<div class="row justify-content-between">
<div class="col-auto">
<div class="col-2 me-0 pe-0 pt-0 mt-0">
<h1>
<div class='input-group'>
<span id="tname">{{ tblname }}</span>
<button class="btn" type="button" onclick="toggle_edit_tname();" id="pencil_button_edit_tname">
<i class="bi bi-pencil"></i>
</button>
</div>
<div class='input-group mt-0'>
<span id="tname">{{ tblname }}</span>
{% block edit_tname %}
{% endblock edit_tname %}
</div>
</h1>
</div>
<div class="col-1 align-self-start align-content-start ms-0 ps-0 mt-0 pt-0">
{% block share %}
{% endblock share %}
</div>
<div class="col-6">
</div>
<!-- Search bar -->
<div class="col-auto">
<div class="btn-toolbar mt-2" role="toolbar">
<div class="btn-group me-2">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#create_entry_modal">
<i class="bi bi-plus-lg"></i>
</button>
</div>
<div class="col-3 align-self-end align-content-end pt-0 mt-0">
<div class="btn-toolbar mt-0 pt-0" role="toolbar">
{% block new_entry %}
{% endblock new_entry %}
<form method="get" action="/table/{{ tblid }}">
<div class="input-group">
<div class="input-group-text dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false">
@@ -81,7 +86,8 @@
</div>
<div class="col-auto">
<div class="btn-toolbar">
<button class="btn" data-bs-toggle="modal" data-bs-target="#create_column_modal" type="button"><i class="bi bi-bookmark-plus"></i></button>
{% block new_column %}
{% endblock new_column %}
<form method="get" action="/table/{{ tblid }}">
<input value="0" name="sort_field" hidden />
<input value="{% if sort_field == 0 %}{{ (sort_dir + 1) % 2}}{% else %}0{% endif %}" name="sort_dir" hidden />
@@ -105,7 +111,7 @@
</div>
<div class="col-auto">
<div class="btn-toolbar">
<button class="btn p-0 me-2" type="button" data-bs-toggle="modal" data-bs-target="#edit_column_modal" onclick="edit_column({{ loop.index0 }});"><i class="bi bi-pencil"></i></button>
<button class="btn p-0 me-2 edit_column_btn" type="button" data-bs-toggle="modal" data-bs-target="#edit_column_modal" onclick="edit_column({{ loop.index0 }});" hidden><i class="bi bi-pencil"></i></button>
<form method="GET" action="/table/{{ tblid }}">
<input value="{{ loop.index }}" name="sort_field" hidden />
<input value="{% if sort_field == loop.index %}{{ (sort_dir + 1) % 2}}{% else %}0{% endif %}" name="sort_dir" hidden />
@@ -136,20 +142,5 @@
</tbody>
</table>
{% endblock body %}
{% block modals %}
<!-- Table specific modals -->
{% include "table_modals" %}
{% endblock modals %}
{% block script %}
<!-- table specific values -->
<script type="text/javascript">
const column_names = [{% for col in column_names %} '{{ col }}', {% endfor %}];
const column_types = [{% for col in column_types %} {{ col }}, {% endfor %}];
const tblid = {{ tblid }};
const tblname = '{{ tblname }}';
</script>
<!-- Table specific functionality -->
<script type="text/javascript" src="/js/table.js"></script>
{% endblock script %}

View File

@@ -0,0 +1,25 @@
{% extends "table_write" %}
{% block edit_tname %}
<button class="btn" type="button" onclick="toggle_edit_tname();" id="pencil_button_edit_tname">
<i class="bi bi-pencil"></i>
</button>
{% endblock edit_tname %}
{% block share %}
<div class="button-group mt-0">
<button class="btn btn-outline-dark mt-0" data-bs-toggle="modal" data-bs-target="#share_table_modal"><i class="bi bi-send-arrow-up"></i></button>
</div>
{% endblock share %}
{% block more_modals %}
{{ super() }}
<!-- Table owned specific modals -->
{% include "table_owned_modals" %}
{% endblock more_modals %}
{% block script %}
<!-- Table owned specific functionality -->
<script type="text/javascript" src="/js/table_owned.js"></script>
{% endblock script %}

View File

@@ -0,0 +1,65 @@
{% block modal_share_table %}
<!-- Share table -->
<div class="modal fade" id="share_table_modal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">
Share table
</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="$( '#form_share_table' ).trigger('reset');"></button>
</div>
<div class="modal-body">
<form action="/table/share" method="post" id="form_share_table" onreset="$('.2benabled').prop('disabled', false);">
<input name="tblid" value="{{ tblid }}" hidden>
{% for user in shared %}
<div class="row py-1">
<div class="col-auto">
<input value="{{ user.username }}" readonly id="form_share_table_user_{{ user.id }}" class="form-control 2benabled" />
<input value="{{ user.id }}" name="sharees" hidden />
</div>
<div class="col-auto form-check form-check-inline pt-2">
<label for="form_share_table_ro_{{ user.id }}" class="form-check-label"> Read only </label>
<input id="form_share_table_ro_{{ user.id }}" type="checkbox" class="form-check-input 2benabled" {% if user.readonly %}checked{% endif %} onchange="$('#form_share_table_action_ro_{{ user.id }}').val($(this).is(':checked'));" />
</div>
<div class="col-auto">
<input id="form_share_table_action_ro_{{ user.id }}" name="readonly" value="{{ user.readonly }}" hidden>
<input id="form_share_table_action_del_{{ user.id }}" name="delete" value="false" hidden>
<button id="form_share_table_delete_button_{{ user.id }}" class="btn btn-outline-danger 2benabled" type="button" onclick="$('#form_share_table_action_del_{{ user.id }}').val(true); $('#form_share_table_ro_{{ user.id }}').prop('disabled', true); $('#form_share_table_user_{{ user.id }}').prop('disabled', true); $('#form_share_table_delete_button_{{ user.id }}').prop('disabled', true);"><i class="bi bi-trash3"></i></button>
</div>
</div>
{% endfor %}
<div class="row py-1">
<div class="col-auto">
<input id="form_share_table_new_user_input" placeholder="User" class="form-control" onchange="query_users($(this).val());" autofocus autocomplete="off" />
<input id="form_share_table_new_user_id" name="new_user" hidden>
<div class="card text-bg-light mt-2">
<ul class="list-group list-group-flush" id="form_share_table_select_new_user">
<!-- users to select from -->
</ul>
</div>
</div>
<div class="col-auto form-check form-check-inline pt-2">
<label for="form_share_table_new_ro">Read-only</label>
<input name="readonly" type="checkbox" class="form-check-input" checked />
</div>
<div class="col-auto">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<div class="row justify-content-betwen">
<div class="col-auto">
</div>
<div class="col-auto">
<button class="btn btn-secondary" data-bs-dismiss="modal" onclick="$('#form_share_table').trigger('reset');"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" onclick="$('#form_share_table').trigger('submit').trigger('reset');"><i class="bi bi-check-lg"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock modal_share_table %}

View File

@@ -0,0 +1 @@
{% extends "table" %}

View File

@@ -0,0 +1,38 @@
{% extends "table" %}
{% block new_entry %}
<div class="btn-group me-2 mt-0">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#create_entry_modal">
<i class="bi bi-plus-lg"></i>
</button>
</div>
{% endblock new_entry %}
{% block new_column %}
<button class="btn" data-bs-toggle="modal" data-bs-target="#create_column_modal" type="button"><i class="bi bi-bookmark-plus"></i></button>
{% endblock new_column %}
{% block edit_column %}
{% endblock edit_column %}
{% block modals %}
<!-- Table write specific modals -->
{% include "table_write_modals" %}
{% endblock modals %}
{% block script %}
<!-- table specific values -->
<script type="text/javascript">
const column_names = [{% for col in column_names %} '{{ col }}', {% endfor %}];
const column_types = [{% for col in column_types %} {{ col }}, {% endfor %}];
const tblid = {{ tblid }};
const tblname = '{{ tblname }}';
</script>
<!-- Table write specific functionality -->
<script type="text/javascript" src="/js/table_write.js"></script>
{% endblock script %}

View File

@@ -23,7 +23,7 @@
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5">Add entry</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="document.getElementById('form_create_entry').reset();"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="$('#form_create_entry').trigger('reset');"></button>
</div>
<div class="modal-body">
<h5>Cell values:</h5>
@@ -49,8 +49,8 @@
<div class="col-auto">
</div>
<div class="col-auto">
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_create_entry').reset();"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="document.getElementById('form_create_entry').submit().reset();"><i class="bi bi-check-lg"></i></button>
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="$('#form_create_entry').trigger('reset');"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="$('#form_create_entry').trigger('submit').trigger('reset');"><i class="bi bi-check-lg"></i></button>
</div>
</div>
</div>
@@ -66,7 +66,7 @@
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5"><div class="row"><div class="col-auto">Row <span id="modal_caller"></span></div><div class="col-auto"><i class="bi bi-pencil-fill"></i></div></div></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="document.getElementById('form_edit_entry').reset();"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="$('#form_edit_entry').trigger('reset');"></button>
</div>
<div class="modal-body">
<form action="/row/edit" method="post" id="form_edit_entry">
@@ -96,8 +96,8 @@
</form>
</div>
<div class="col-auto">
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_edit_entry').reset();"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="submit_and_reset('form_edit_entry');" class="bi bi-check-lg"></i></button>
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="$('#form_edit_entry').trigger('reset');"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="$('#form_edit_entry').trigger('submit').trigger('reset');"><i class="bi bi-check-lg"></i></button>
</div>
</div>
</div>
@@ -108,7 +108,7 @@
{% block modal_new_column %}
<!-- Add column -->
<div class="modal fade" id="create_column_modal" tabindex="-1" aria-hidden="true" onclick="document.getElementById('form_create_column').reset();">
<div class="modal fade" id="create_column_modal" tabindex="-1" aria-hidden="true" onclick="$('#form_create_column').trigger('reset');">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
@@ -137,8 +137,8 @@
</div>
<div class="modal-footer">
<div class="col-auto">
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_create_column').reset();"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="submit_and_reset('form_create_column');"><i class="bi bi-check-lg"></i></button>
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="$'#form_create_column').trigger('reset');"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="$('#form_create_column').trigger('submit').trigger('reset');"><i class="bi bi-check-lg"></i></button>
</div>
</div>
</div>
@@ -153,7 +153,7 @@
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5"><div class="row"><div class="col-auto"><span id="modal_caller"></span></div><div class="col-auto"><i class="bi bi-pencil-fill"></i></div></div></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" onclick="$('#form_edit_column').trigger('reset');"></button>
</div>
<div class="modal-body">
<form action="/column/edit" method="post" id="form_edit_column">
@@ -186,8 +186,8 @@
</form>
</div>
<div class="col-auto">
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="document.getElementById('form_edit_column').reset();"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="submit_and_reset('form_edit_column');"><i class="bi bi-check-lg"></i></button>
<button class="btn btn-secondary" data-bs-dismiss="modal" type="button" onclick="$('#form_edit_column').trigger('reset');"><i class="bi bi-x"></i></button>
<button class="btn btn-primary" type="button" onclick="$('#form_edit_column').trigger('submit').trigger('reset');"><i class="bi bi-check-lg"></i></button>
</div>
</div>
</div>