Răsfoiți Sursa

Create form for setting table preferences

Jeremy Stretch 5 ani în urmă
părinte
comite
e8d493578b

+ 23 - 0
netbox/templates/inc/table_config_form.html

@@ -0,0 +1,23 @@
+{% load form_helpers %}
+
+<button type="button" class="btn btn-primary btn-xs" data-toggle="modal" data-target="#tableconfig" title="Configure table"><i class="fa fa-cog"></i></button>
+<div class="modal fade" tabindex="-1" id="tableconfig">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                <h4 class="modal-title">Table Configuration</h4>
+            </div>
+            <div class="modal-body">
+                <form action="?return_url={{ request.path }}" method="post" class="form-horizontal">
+                    {% csrf_token %}
+                    {% render_form table_config_form %}
+                    <div class="text-right">
+                        <input type="submit" class="btn btn-primary" name="set" value="Save" />
+                        <input type="submit" class="btn btn-danger" name="clear" value="Clear" />
+                    </div>
+                </form>
+            </div>
+        </div>
+    </div>
+</div>

+ 3 - 0
netbox/templates/utilities/obj_list.html

@@ -18,6 +18,9 @@
 <h1>{% block title %}{{ content_type.model_class|meta:"verbose_name_plural"|bettertitle }}{% endblock %}</h1>
 <div class="row">
     <div class="col-md-{% if filter_form %}9{% else %}12{% endif %}">
+        {% if table_config_form %}
+            {% include 'inc/table_config_form.html' %}
+        {% endif %}
         {% with bulk_edit_url=content_type.model_class|url_name:"bulk_edit" bulk_delete_url=content_type.model_class|url_name:"bulk_delete" %}
         {% if permissions.change or permissions.delete %}
             <form method="post" class="form form-horizontal">

+ 42 - 13
netbox/utilities/forms.py

@@ -137,6 +137,27 @@ def form_from_model(model, fields):
     return type('FormFromModel', (forms.Form,), form_fields)
 
 
+def apply_bootstrap_classes(form):
+    """
+    Apply Bootstrap CSS classes to form elements.
+    """
+    exempt_widgets = [
+        forms.CheckboxInput,
+        forms.ClearableFileInput,
+        forms.FileInput,
+        forms.RadioSelect
+    ]
+
+    for field_name, field in form.fields.items():
+        if field.widget.__class__ not in exempt_widgets:
+            css = field.widget.attrs.get('class', '')
+            field.widget.attrs['class'] = ' '.join([css, 'form-control']).strip()
+        if field.required and not isinstance(field.widget, forms.FileInput):
+            field.widget.attrs['required'] = 'required'
+        if 'placeholder' not in field.widget.attrs:
+            field.widget.attrs['placeholder'] = field.label
+
+
 #
 # Widgets
 #
@@ -663,19 +684,7 @@ class BootstrapMixin(forms.BaseForm):
     """
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-
-        exempt_widgets = [
-            forms.CheckboxInput, forms.ClearableFileInput, forms.FileInput, forms.RadioSelect
-        ]
-
-        for field_name, field in self.fields.items():
-            if field.widget.__class__ not in exempt_widgets:
-                css = field.widget.attrs.get('class', '')
-                field.widget.attrs['class'] = ' '.join([css, 'form-control']).strip()
-            if field.required and not isinstance(field.widget, forms.FileInput):
-                field.widget.attrs['required'] = 'required'
-            if 'placeholder' not in field.widget.attrs:
-                field.widget.attrs['placeholder'] = field.label
+        apply_bootstrap_classes(self)
 
 
 class ReturnURLForm(forms.Form):
@@ -752,3 +761,23 @@ class ImportForm(BootstrapMixin, forms.Form):
                 raise forms.ValidationError({
                     'data': "Invalid YAML data: {}".format(err)
                 })
+
+
+class TableConfigForm(forms.Form):
+    """
+    Form for configuring user's table preferences.
+    """
+    def __init__(self, table, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        field_name = f"tables.{table.__class__.__name__}.columns"
+        self.fields[field_name] = forms.MultipleChoiceField(
+            choices=table.configurable_columns,
+            initial=table.visible_columns,
+            label='Columns',
+            widget=forms.SelectMultiple(
+                attrs={'size': 10}
+            )
+        )
+
+        apply_bootstrap_classes(self)

+ 18 - 4
netbox/utilities/tables.py

@@ -6,6 +6,11 @@ class BaseTable(tables.Table):
     """
     Default table for object lists
     """
+    class Meta:
+        attrs = {
+            'class': 'table table-hover table-headings',
+        }
+
     def __init__(self, *args, columns=None, **kwargs):
         super().__init__(*args, **kwargs)
 
@@ -29,10 +34,19 @@ class BaseTable(tables.Table):
                 self.base_columns['pk'] = pk
                 self.sequence.insert(0, 'pk')
 
-    class Meta:
-        attrs = {
-            'class': 'table table-hover table-headings',
-        }
+    @property
+    def configurable_columns(self):
+        selected_columns = [
+            (name, self.columns[name].verbose_name) for name in self.sequence if name != 'pk'
+        ]
+        available_columns = [
+            (name, column.verbose_name) for name, column in self.columns.items() if name not in self.sequence and name != 'pk'
+        ]
+        return selected_columns + available_columns
+
+    @property
+    def visible_columns(self):
+        return [name for name in self.sequence if self.columns[name].visible]
 
 
 class ToggleColumn(tables.CheckBoxColumn):

+ 6 - 1
netbox/utilities/views.py

@@ -24,7 +24,7 @@ from django_tables2 import RequestConfig
 from extras.models import CustomField, CustomFieldValue, ExportTemplate
 from extras.querysets import CustomFieldQueryset
 from utilities.exceptions import AbortTransaction
-from utilities.forms import BootstrapMixin, CSVDataField
+from utilities.forms import BootstrapMixin, CSVDataField, TableConfigForm
 from utilities.utils import csv_format, prepare_cloned_fields
 from .error_handlers import handle_protectederror
 from .forms import ConfirmationForm, ImportForm
@@ -176,11 +176,16 @@ class ObjectListView(View):
         }
         RequestConfig(request, paginate).configure(table)
 
+        table_config_form = TableConfigForm(
+            table=table
+        )
+
         context = {
             'content_type': content_type,
             'table': table,
             'permissions': permissions,
             'action_buttons': self.action_buttons,
+            'table_config_form': table_config_form,
             'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
         }
         context.update(self.extra_context())