Przeglądaj źródła

Create form for setting table preferences

Jeremy Stretch 5 lat temu
rodzic
commit
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>
 <h1>{% block title %}{{ content_type.model_class|meta:"verbose_name_plural"|bettertitle }}{% endblock %}</h1>
 <div class="row">
 <div class="row">
     <div class="col-md-{% if filter_form %}9{% else %}12{% endif %}">
     <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" %}
         {% 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 %}
         {% if permissions.change or permissions.delete %}
             <form method="post" class="form form-horizontal">
             <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)
     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
 # Widgets
 #
 #
@@ -663,19 +684,7 @@ class BootstrapMixin(forms.BaseForm):
     """
     """
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
         super().__init__(*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):
 class ReturnURLForm(forms.Form):
@@ -752,3 +761,23 @@ class ImportForm(BootstrapMixin, forms.Form):
                 raise forms.ValidationError({
                 raise forms.ValidationError({
                     'data': "Invalid YAML data: {}".format(err)
                     '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
     Default table for object lists
     """
     """
+    class Meta:
+        attrs = {
+            'class': 'table table-hover table-headings',
+        }
+
     def __init__(self, *args, columns=None, **kwargs):
     def __init__(self, *args, columns=None, **kwargs):
         super().__init__(*args, **kwargs)
         super().__init__(*args, **kwargs)
 
 
@@ -29,10 +34,19 @@ class BaseTable(tables.Table):
                 self.base_columns['pk'] = pk
                 self.base_columns['pk'] = pk
                 self.sequence.insert(0, '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):
 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.models import CustomField, CustomFieldValue, ExportTemplate
 from extras.querysets import CustomFieldQueryset
 from extras.querysets import CustomFieldQueryset
 from utilities.exceptions import AbortTransaction
 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 utilities.utils import csv_format, prepare_cloned_fields
 from .error_handlers import handle_protectederror
 from .error_handlers import handle_protectederror
 from .forms import ConfirmationForm, ImportForm
 from .forms import ConfirmationForm, ImportForm
@@ -176,11 +176,16 @@ class ObjectListView(View):
         }
         }
         RequestConfig(request, paginate).configure(table)
         RequestConfig(request, paginate).configure(table)
 
 
+        table_config_form = TableConfigForm(
+            table=table
+        )
+
         context = {
         context = {
             'content_type': content_type,
             'content_type': content_type,
             'table': table,
             'table': table,
             'permissions': permissions,
             'permissions': permissions,
             'action_buttons': self.action_buttons,
             '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,
             'filter_form': self.filterset_form(request.GET, label_suffix='') if self.filterset_form else None,
         }
         }
         context.update(self.extra_context())
         context.update(self.extra_context())