Convert extras to new declarative UI layout

This commit is contained in:
Brian Tiemann
2026-03-26 15:09:03 -04:00
parent f5c97e367c
commit 1c200f403a
41 changed files with 937 additions and 1193 deletions

View File

@@ -2,16 +2,59 @@ from django.contrib.contenttypes.models import ContentType
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from netbox.ui import actions, panels from netbox.ui import actions, attrs, panels
from netbox.ui.actions import CopyContent
from utilities.data import resolve_attr_path from utilities.data import resolve_attr_path
__all__ = ( __all__ = (
'ConfigContextAssignmentPanel',
'ConfigContextPanel',
'ConfigContextProfilePanel',
'ConfigContextProfileSchemaPanel',
'ConfigTemplatePanel',
'CustomFieldBehaviorPanel',
'CustomFieldChoiceSetChoicesPanel',
'CustomFieldChoiceSetPanel',
'CustomFieldObjectTypesPanel',
'CustomFieldPanel',
'CustomFieldRelatedObjectsPanel',
'CustomFieldValidationPanel',
'CustomFieldsPanel', 'CustomFieldsPanel',
'CustomLinkPanel',
'EventRuleActionPanel',
'EventRuleEventTypesPanel',
'EventRulePanel',
'ExportTemplatePanel',
'ImageAttachmentFilePanel',
'ImageAttachmentImagePanel',
'ImageAttachmentPanel',
'ImageAttachmentsPanel', 'ImageAttachmentsPanel',
'JournalEntryPanel',
'NotificationGroupGroupsPanel',
'NotificationGroupPanel',
'NotificationGroupUsersPanel',
'ObjectTypesPanel',
'SavedFilterObjectTypesPanel',
'SavedFilterPanel',
'SyncCodePanel',
'TableConfigColumnsPanel',
'TableConfigOrderingPanel',
'TableConfigPanel',
'TagItemTypesPanel',
'TagObjectTypesPanel',
'TagPanel',
'TagsPanel', 'TagsPanel',
'TextCodePanel',
'WebhookHTTPPanel',
'WebhookPanel',
'WebhookSSLPanel',
) )
#
# Generic panels
#
class CustomFieldsPanel(panels.ObjectPanel): class CustomFieldsPanel(panels.ObjectPanel):
""" """
A panel showing the value of all custom fields defined on an object. A panel showing the value of all custom fields defined on an object.
@@ -73,3 +116,426 @@ class TagsPanel(panels.ObjectPanel):
**super().get_context(context), **super().get_context(context),
'object': resolve_attr_path(context, self.accessor), 'object': resolve_attr_path(context, self.accessor),
} }
class ObjectTypesPanel(panels.ObjectPanel):
"""
A panel listing the object types assigned to the object.
"""
template_name = 'extras/panels/object_types.html'
title = _('Object Types')
class TextCodePanel(panels.ObjectPanel):
"""
A panel displaying a text field as a pre-formatted code block.
"""
template_name = 'extras/panels/text_code.html'
def __init__(self, field_name, **kwargs):
super().__init__(**kwargs)
self.field_name = field_name
def get_context(self, context):
return {
**super().get_context(context),
'value': getattr(context.get('object'), self.field_name, None),
}
class SyncCodePanel(panels.ObjectPanel):
"""
A panel displaying a text field with a sync warning included.
"""
template_name = 'extras/panels/sync_code.html'
def __init__(self, field_name, **kwargs):
super().__init__(**kwargs)
self.field_name = field_name
def get_context(self, context):
return {
**super().get_context(context),
'value': getattr(context.get('object'), self.field_name, None),
}
#
# CustomField panels
#
class CustomFieldPanel(panels.ObjectAttributesPanel):
title = _('Custom Field')
name = attrs.TextAttr('name')
type = attrs.TemplatedAttr('type', label=_('Type'), template_name='extras/customfield/attrs/type.html')
label = attrs.TextAttr('label')
group_name = attrs.TextAttr('group_name', label=_('Group Name'))
description = attrs.TextAttr('description')
required = attrs.BooleanAttr('required')
unique = attrs.BooleanAttr('unique', label=_('Must be Unique'))
is_cloneable = attrs.BooleanAttr('is_cloneable', label=_('Cloneable'))
choice_set = attrs.TemplatedAttr(
'choice_set',
label=_('Choice Set'),
template_name='extras/customfield/attrs/choice_set.html',
)
default = attrs.TextAttr('default', label=_('Default Value'))
related_object_filter = attrs.TemplatedAttr(
'related_object_filter',
label=_('Related object filter'),
template_name='extras/customfield/attrs/related_object_filter.html',
)
class CustomFieldBehaviorPanel(panels.ObjectAttributesPanel):
title = _('Behavior')
search_weight = attrs.TemplatedAttr(
'search_weight',
label=_('Search Weight'),
template_name='extras/customfield/attrs/search_weight.html',
)
filter_logic = attrs.ChoiceAttr('filter_logic', label=_('Filter Logic'))
weight = attrs.NumericAttr('weight', label=_('Display Weight'))
ui_visible = attrs.ChoiceAttr('ui_visible', label=_('UI Visible'))
ui_editable = attrs.ChoiceAttr('ui_editable', label=_('UI Editable'))
class CustomFieldValidationPanel(panels.ObjectAttributesPanel):
title = _('Validation Rules')
validation_minimum = attrs.NumericAttr('validation_minimum', label=_('Minimum Value'))
validation_maximum = attrs.NumericAttr('validation_maximum', label=_('Maximum Value'))
validation_regex = attrs.TextAttr(
'validation_regex',
label=_('Regular Expression'),
style='font-monospace',
)
class CustomFieldObjectTypesPanel(panels.ObjectPanel):
template_name = 'extras/panels/object_types.html'
title = _('Object Types')
class CustomFieldRelatedObjectsPanel(panels.ObjectPanel):
template_name = 'extras/panels/customfield_related_objects.html'
title = _('Related Objects')
def get_context(self, context):
return {
**super().get_context(context),
'related_models': context.get('related_models'),
}
#
# CustomFieldChoiceSet panels
#
class CustomFieldChoiceSetPanel(panels.ObjectAttributesPanel):
title = _('Custom Field Choice Set')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
base_choices = attrs.ChoiceAttr('base_choices', label=_('Base Choices'))
order_alphabetically = attrs.BooleanAttr('order_alphabetically', label=_('Order Alphabetically'))
choices_for = attrs.RelatedObjectListAttr('choices_for', linkify=True, label=_('Used by'))
class CustomFieldChoiceSetChoicesPanel(panels.ObjectPanel):
template_name = 'extras/panels/customfieldchoiceset_choices.html'
def get_context(self, context):
obj = context.get('object')
total = len(obj.choices) if obj else 0
return {
**super().get_context(context),
'title': f'{_("Choices")} ({total})',
'choices': context.get('choices'),
}
#
# CustomLink panels
#
class CustomLinkPanel(panels.ObjectAttributesPanel):
title = _('Custom Link')
name = attrs.TextAttr('name')
enabled = attrs.BooleanAttr('enabled')
group_name = attrs.TextAttr('group_name', label=_('Group Name'))
weight = attrs.NumericAttr('weight')
button_class = attrs.ChoiceAttr('button_class', label=_('Button Class'))
new_window = attrs.BooleanAttr('new_window', label=_('New Window'))
#
# ExportTemplate panels
#
class ExportTemplatePanel(panels.ObjectAttributesPanel):
title = _('Export Template')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
mime_type = attrs.TextAttr('mime_type', label=_('MIME Type'))
file_name = attrs.TextAttr('file_name', label=_('File Name'))
file_extension = attrs.TextAttr('file_extension', label=_('File Extension'))
as_attachment = attrs.BooleanAttr('as_attachment', label=_('Attachment'))
#
# SavedFilter panels
#
class SavedFilterPanel(panels.ObjectAttributesPanel):
title = _('Saved Filter')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
user = attrs.TextAttr('user')
enabled = attrs.BooleanAttr('enabled')
shared = attrs.BooleanAttr('shared')
weight = attrs.NumericAttr('weight')
class SavedFilterObjectTypesPanel(panels.ObjectPanel):
template_name = 'extras/panels/savedfilter_object_types.html'
title = _('Assigned Models')
#
# TableConfig panels
#
class TableConfigPanel(panels.ObjectAttributesPanel):
title = _('Table Config')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
object_type = attrs.TextAttr('object_type', label=_('Object Type'))
table = attrs.TextAttr('table')
user = attrs.TextAttr('user')
enabled = attrs.BooleanAttr('enabled')
shared = attrs.BooleanAttr('shared')
weight = attrs.NumericAttr('weight')
class TableConfigColumnsPanel(panels.ObjectPanel):
template_name = 'extras/panels/tableconfig_columns.html'
title = _('Columns Displayed')
def get_context(self, context):
return {
**super().get_context(context),
'columns': context.get('columns'),
}
class TableConfigOrderingPanel(panels.ObjectPanel):
template_name = 'extras/panels/tableconfig_ordering.html'
title = _('Ordering')
def get_context(self, context):
return {
**super().get_context(context),
'columns': context.get('columns'),
}
#
# NotificationGroup panels
#
class NotificationGroupPanel(panels.ObjectAttributesPanel):
title = _('Notification Group')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
class NotificationGroupGroupsPanel(panels.ObjectPanel):
template_name = 'extras/panels/notificationgroup_groups.html'
title = _('Groups')
class NotificationGroupUsersPanel(panels.ObjectPanel):
template_name = 'extras/panels/notificationgroup_users.html'
title = _('Users')
#
# Webhook panels
#
class WebhookPanel(panels.ObjectAttributesPanel):
title = _('Webhook')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
class WebhookHTTPPanel(panels.ObjectAttributesPanel):
title = _('HTTP Request')
http_method = attrs.ChoiceAttr('http_method', label=_('HTTP Method'))
payload_url = attrs.TextAttr('payload_url', label=_('Payload URL'), style='font-monospace')
http_content_type = attrs.TextAttr('http_content_type', label=_('HTTP Content Type'))
secret = attrs.TextAttr('secret', label=_('Secret'))
class WebhookSSLPanel(panels.ObjectAttributesPanel):
title = _('SSL')
ssl_verification = attrs.BooleanAttr('ssl_verification', label=_('SSL Verification'))
ca_file_path = attrs.TextAttr('ca_file_path', label=_('CA File Path'))
#
# EventRule panels
#
class EventRulePanel(panels.ObjectAttributesPanel):
title = _('Event Rule')
name = attrs.TextAttr('name')
enabled = attrs.BooleanAttr('enabled')
description = attrs.TextAttr('description')
class EventRuleEventTypesPanel(panels.ObjectPanel):
template_name = 'extras/panels/eventrule_event_types.html'
title = _('Event Types')
def get_context(self, context):
return {
**super().get_context(context),
'registry': context.get('registry'),
}
class EventRuleActionPanel(panels.ObjectAttributesPanel):
title = _('Action')
action_type = attrs.ChoiceAttr('action_type', label=_('Type'))
action_object = attrs.RelatedObjectAttr('action_object', linkify=True, label=_('Object'))
action_data = attrs.TemplatedAttr(
'action_data',
label=_('Data'),
template_name='extras/eventrule/attrs/action_data.html',
)
#
# Tag panels
#
class TagPanel(panels.ObjectAttributesPanel):
title = _('Tag')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
color = attrs.ColorAttr('color')
weight = attrs.NumericAttr('weight')
class TagObjectTypesPanel(panels.ObjectPanel):
template_name = 'extras/panels/tag_object_types.html'
title = _('Allowed Object Types')
class TagItemTypesPanel(panels.ObjectPanel):
template_name = 'extras/panels/tag_item_types.html'
title = _('Tagged Item Types')
def get_context(self, context):
return {
**super().get_context(context),
'object_types': context.get('object_types'),
}
#
# ConfigContextProfile panels
#
class ConfigContextProfilePanel(panels.ObjectAttributesPanel):
title = _('Config Context Profile')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
class ConfigContextProfileSchemaPanel(panels.ObjectPanel):
template_name = 'extras/panels/configcontextprofile_schema.html'
title = _('JSON Schema')
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.actions = [CopyContent('schema')]
#
# ConfigContext panels
#
class ConfigContextPanel(panels.ObjectAttributesPanel):
title = _('Config Context')
name = attrs.TextAttr('name')
weight = attrs.NumericAttr('weight')
profile = attrs.RelatedObjectAttr('profile', linkify=True)
description = attrs.TextAttr('description')
is_active = attrs.BooleanAttr('is_active', label=_('Active'))
class ConfigContextAssignmentPanel(panels.ObjectPanel):
template_name = 'extras/panels/configcontext_assignment.html'
title = _('Assignment')
def get_context(self, context):
return {
**super().get_context(context),
'assigned_objects': context.get('assigned_objects'),
}
#
# ConfigTemplate panels
#
class ConfigTemplatePanel(panels.ObjectAttributesPanel):
title = _('Config Template')
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
mime_type = attrs.TextAttr('mime_type', label=_('MIME Type'))
file_name = attrs.TextAttr('file_name', label=_('File Name'))
file_extension = attrs.TextAttr('file_extension', label=_('File Extension'))
as_attachment = attrs.BooleanAttr('as_attachment', label=_('Attachment'))
data_source = attrs.RelatedObjectAttr('data_source', linkify=True, label=_('Data Source'))
data_file = attrs.TemplatedAttr(
'data_path',
label=_('Data File'),
template_name='extras/configtemplate/attrs/data_file.html',
)
data_synced = attrs.DateTimeAttr('data_synced', label=_('Data Synced'))
auto_sync_enabled = attrs.BooleanAttr('auto_sync_enabled', label=_('Auto Sync Enabled'))
#
# ImageAttachment panels
#
class ImageAttachmentPanel(panels.ObjectAttributesPanel):
title = _('Image Attachment')
parent = attrs.RelatedObjectAttr('parent', linkify=True, label=_('Parent Object'))
name = attrs.TextAttr('name')
description = attrs.TextAttr('description')
class ImageAttachmentFilePanel(panels.ObjectPanel):
template_name = 'extras/panels/imageattachment_file.html'
title = _('File')
class ImageAttachmentImagePanel(panels.ObjectPanel):
template_name = 'extras/panels/imageattachment_image.html'
title = _('Image')
#
# JournalEntry panels
#
class JournalEntryPanel(panels.ObjectAttributesPanel):
title = _('Journal Entry')
assigned_object = attrs.RelatedObjectAttr('assigned_object', linkify=True, label=_('Object'))
created = attrs.DateTimeAttr('created', spec='minutes')
created_by = attrs.TextAttr('created_by', label=_('Created By'))
kind = attrs.ChoiceAttr('kind')

View File

@@ -11,6 +11,7 @@ from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.utils.translation import gettext_lazy as _l
from django.views.generic import View from django.views.generic import View
from jinja2.exceptions import TemplateError from jinja2.exceptions import TemplateError
@@ -23,6 +24,8 @@ from extras.dashboard.forms import DashboardWidgetAddForm, DashboardWidgetForm
from extras.dashboard.utils import get_widget_class from extras.dashboard.utils import get_widget_class
from extras.utils import SharedObjectViewMixin from extras.utils import SharedObjectViewMixin
from netbox.object_actions import * from netbox.object_actions import *
from netbox.ui import layout
from netbox.ui.panels import CommentsPanel, ContextTablePanel, JSONPanel, TemplatePanel
from netbox.views import generic from netbox.views import generic
from netbox.views.generic.mixins import TableMixin from netbox.views.generic.mixins import TableMixin
from utilities.forms import ConfirmationForm, get_field_value from utilities.forms import ConfirmationForm, get_field_value
@@ -40,6 +43,7 @@ from . import filtersets, forms, tables
from .constants import LOG_LEVEL_RANK from .constants import LOG_LEVEL_RANK
from .models import * from .models import *
from .tables import ReportResultsTable, ScriptJobTable, ScriptResultsTable from .tables import ReportResultsTable, ScriptJobTable, ScriptResultsTable
from .ui import panels as ui_panels
# #
# Custom fields # Custom fields
@@ -57,6 +61,18 @@ class CustomFieldListView(generic.ObjectListView):
@register_model_view(CustomField) @register_model_view(CustomField)
class CustomFieldView(generic.ObjectView): class CustomFieldView(generic.ObjectView):
queryset = CustomField.objects.select_related('choice_set') queryset = CustomField.objects.select_related('choice_set')
layout = layout.SimpleLayout(
left_panels=[
ui_panels.CustomFieldPanel(),
ui_panels.CustomFieldBehaviorPanel(),
CommentsPanel(),
],
right_panels=[
ui_panels.CustomFieldObjectTypesPanel(),
ui_panels.CustomFieldValidationPanel(),
ui_panels.CustomFieldRelatedObjectsPanel(),
],
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
related_models = () related_models = ()
@@ -128,6 +144,14 @@ class CustomFieldChoiceSetListView(generic.ObjectListView):
@register_model_view(CustomFieldChoiceSet) @register_model_view(CustomFieldChoiceSet)
class CustomFieldChoiceSetView(generic.ObjectView): class CustomFieldChoiceSetView(generic.ObjectView):
queryset = CustomFieldChoiceSet.objects.all() queryset = CustomFieldChoiceSet.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.CustomFieldChoiceSetPanel(),
],
right_panels=[
ui_panels.CustomFieldChoiceSetChoicesPanel(),
],
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
@@ -203,6 +227,16 @@ class CustomLinkListView(generic.ObjectListView):
@register_model_view(CustomLink) @register_model_view(CustomLink)
class CustomLinkView(generic.ObjectView): class CustomLinkView(generic.ObjectView):
queryset = CustomLink.objects.all() queryset = CustomLink.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.CustomLinkPanel(),
ui_panels.ObjectTypesPanel(title=_l('Assigned Models')),
],
right_panels=[
ui_panels.TextCodePanel('link_text', title=_l('Link Text')),
ui_panels.TextCodePanel('link_url', title=_l('Link URL')),
],
)
@register_model_view(CustomLink, 'add', detail=False) @register_model_view(CustomLink, 'add', detail=False)
@@ -260,6 +294,19 @@ class ExportTemplateListView(generic.ObjectListView):
@register_model_view(ExportTemplate) @register_model_view(ExportTemplate)
class ExportTemplateView(generic.ObjectView): class ExportTemplateView(generic.ObjectView):
queryset = ExportTemplate.objects.all() queryset = ExportTemplate.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.ExportTemplatePanel(),
TemplatePanel('core/inc/datafile_panel.html'),
],
right_panels=[
ui_panels.ObjectTypesPanel(title=_l('Assigned Models')),
JSONPanel('environment_params', title=_l('Environment Parameters')),
],
bottom_panels=[
ui_panels.SyncCodePanel('template_code', title=_l('Template')),
],
)
@register_model_view(ExportTemplate, 'add', detail=False) @register_model_view(ExportTemplate, 'add', detail=False)
@@ -321,6 +368,15 @@ class SavedFilterListView(SharedObjectViewMixin, generic.ObjectListView):
@register_model_view(SavedFilter) @register_model_view(SavedFilter)
class SavedFilterView(SharedObjectViewMixin, generic.ObjectView): class SavedFilterView(SharedObjectViewMixin, generic.ObjectView):
queryset = SavedFilter.objects.all() queryset = SavedFilter.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.SavedFilterPanel(),
ui_panels.SavedFilterObjectTypesPanel(),
],
right_panels=[
JSONPanel('parameters', title=_l('Parameters')),
],
)
@register_model_view(SavedFilter, 'add', detail=False) @register_model_view(SavedFilter, 'add', detail=False)
@@ -383,6 +439,15 @@ class TableConfigListView(SharedObjectViewMixin, generic.ObjectListView):
@register_model_view(TableConfig) @register_model_view(TableConfig)
class TableConfigView(SharedObjectViewMixin, generic.ObjectView): class TableConfigView(SharedObjectViewMixin, generic.ObjectView):
queryset = TableConfig.objects.all() queryset = TableConfig.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.TableConfigPanel(),
],
right_panels=[
ui_panels.TableConfigColumnsPanel(),
ui_panels.TableConfigOrderingPanel(),
],
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
table = instance.table_class([]) table = instance.table_class([])
@@ -476,6 +541,15 @@ class NotificationGroupListView(generic.ObjectListView):
@register_model_view(NotificationGroup) @register_model_view(NotificationGroup)
class NotificationGroupView(generic.ObjectView): class NotificationGroupView(generic.ObjectView):
queryset = NotificationGroup.objects.all() queryset = NotificationGroup.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.NotificationGroupPanel(),
],
right_panels=[
ui_panels.NotificationGroupGroupsPanel(),
ui_panels.NotificationGroupUsersPanel(),
],
)
@register_model_view(NotificationGroup, 'add', detail=False) @register_model_view(NotificationGroup, 'add', detail=False)
@@ -660,6 +734,19 @@ class WebhookListView(generic.ObjectListView):
@register_model_view(Webhook) @register_model_view(Webhook)
class WebhookView(generic.ObjectView): class WebhookView(generic.ObjectView):
queryset = Webhook.objects.all() queryset = Webhook.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.WebhookPanel(),
ui_panels.WebhookHTTPPanel(),
ui_panels.WebhookSSLPanel(),
],
right_panels=[
ui_panels.TextCodePanel('additional_headers', title=_l('Additional Headers')),
ui_panels.TextCodePanel('body_template', title=_l('Body Template')),
ui_panels.CustomFieldsPanel(),
ui_panels.TagsPanel(),
],
)
@register_model_view(Webhook, 'add', detail=False) @register_model_view(Webhook, 'add', detail=False)
@@ -716,6 +803,19 @@ class EventRuleListView(generic.ObjectListView):
@register_model_view(EventRule) @register_model_view(EventRule)
class EventRuleView(generic.ObjectView): class EventRuleView(generic.ObjectView):
queryset = EventRule.objects.all() queryset = EventRule.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.EventRulePanel(),
ui_panels.ObjectTypesPanel(),
ui_panels.EventRuleEventTypesPanel(),
],
right_panels=[
JSONPanel('conditions', title=_l('Conditions')),
ui_panels.EventRuleActionPanel(),
ui_panels.CustomFieldsPanel(),
ui_panels.TagsPanel(),
],
)
@register_model_view(EventRule, 'add', detail=False) @register_model_view(EventRule, 'add', detail=False)
@@ -774,6 +874,18 @@ class TagListView(generic.ObjectListView):
@register_model_view(Tag) @register_model_view(Tag)
class TagView(generic.ObjectView): class TagView(generic.ObjectView):
queryset = Tag.objects.all() queryset = Tag.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.TagPanel(),
],
right_panels=[
ui_panels.TagObjectTypesPanel(),
ui_panels.TagItemTypesPanel(),
],
bottom_panels=[
ContextTablePanel('taggeditem_table', title=_l('Tagged Objects')),
],
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
tagged_items = TaggedItem.objects.filter(tag=instance) tagged_items = TaggedItem.objects.filter(tag=instance)
@@ -853,6 +965,18 @@ class ConfigContextProfileListView(generic.ObjectListView):
@register_model_view(ConfigContextProfile) @register_model_view(ConfigContextProfile)
class ConfigContextProfileView(generic.ObjectView): class ConfigContextProfileView(generic.ObjectView):
queryset = ConfigContextProfile.objects.all() queryset = ConfigContextProfile.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.ConfigContextProfilePanel(),
TemplatePanel('core/inc/datafile_panel.html'),
ui_panels.CustomFieldsPanel(),
ui_panels.TagsPanel(),
CommentsPanel(),
],
right_panels=[
ui_panels.ConfigContextProfileSchemaPanel(),
],
)
@register_model_view(ConfigContextProfile, 'add', detail=False) @register_model_view(ConfigContextProfile, 'add', detail=False)
@@ -915,6 +1039,16 @@ class ConfigContextListView(generic.ObjectListView):
@register_model_view(ConfigContext) @register_model_view(ConfigContext)
class ConfigContextView(generic.ObjectView): class ConfigContextView(generic.ObjectView):
queryset = ConfigContext.objects.all() queryset = ConfigContext.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.ConfigContextPanel(),
TemplatePanel('core/inc/datafile_panel.html'),
ui_panels.ConfigContextAssignmentPanel(),
],
right_panels=[
TemplatePanel('extras/panels/configcontext_data.html'),
],
)
def get_extra_context(self, request, instance): def get_extra_context(self, request, instance):
# Gather assigned objects for parsing in the template # Gather assigned objects for parsing in the template
@@ -1034,6 +1168,18 @@ class ConfigTemplateListView(generic.ObjectListView):
@register_model_view(ConfigTemplate) @register_model_view(ConfigTemplate)
class ConfigTemplateView(generic.ObjectView): class ConfigTemplateView(generic.ObjectView):
queryset = ConfigTemplate.objects.all() queryset = ConfigTemplate.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.ConfigTemplatePanel(),
ui_panels.TagsPanel(),
],
right_panels=[
JSONPanel('environment_params', title=_l('Environment Parameters')),
],
bottom_panels=[
ui_panels.SyncCodePanel('template_code', title=_l('Template')),
],
)
@register_model_view(ConfigTemplate, 'add', detail=False) @register_model_view(ConfigTemplate, 'add', detail=False)
@@ -1151,6 +1297,17 @@ class ImageAttachmentListView(generic.ObjectListView):
@register_model_view(ImageAttachment) @register_model_view(ImageAttachment)
class ImageAttachmentView(generic.ObjectView): class ImageAttachmentView(generic.ObjectView):
queryset = ImageAttachment.objects.all() queryset = ImageAttachment.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.ImageAttachmentPanel(),
],
right_panels=[
ui_panels.ImageAttachmentFilePanel(),
],
bottom_panels=[
ui_panels.ImageAttachmentImagePanel(),
],
)
@register_model_view(ImageAttachment, 'add', detail=False) @register_model_view(ImageAttachment, 'add', detail=False)
@@ -1215,6 +1372,16 @@ class JournalEntryListView(generic.ObjectListView):
@register_model_view(JournalEntry) @register_model_view(JournalEntry)
class JournalEntryView(generic.ObjectView): class JournalEntryView(generic.ObjectView):
queryset = JournalEntry.objects.all() queryset = JournalEntry.objects.all()
layout = layout.SimpleLayout(
left_panels=[
ui_panels.JournalEntryPanel(),
ui_panels.CustomFieldsPanel(),
ui_panels.TagsPanel(),
],
right_panels=[
CommentsPanel(),
],
)
@register_model_view(JournalEntry, 'add', detail=False) @register_model_view(JournalEntry, 'add', detail=False)

View File

@@ -1,62 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-12 col-md-5">
<div class="card">
<h2 class="card-header">{% trans "Config Context" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Weight" %}</th>
<td>{{ object.weight }}</td>
</tr>
<tr>
<th scope="row">{% trans "Profile" %}</th>
<td>{{ object.profile|linkify|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Active" %}</th>
<td>{% checkmark object.is_active %}</td>
</tr>
</table>
</div>
{% include 'core/inc/datafile_panel.html' %}
<div class="card">
<h2 class="card-header">{% trans "Assignment" %}</h2>
<table class="table table-hover attr-table">
{% for title, objects in assigned_objects %}
<tr>
<th scope="row">{{ title }}</th>
<td>
<ul class="list-unstyled mb-0">
{% for object in objects %}
<li>{{ object|linkify }}</li>
{% empty %}
<li class="text-muted">{% trans "None" %}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</table>
</div>
</div>
<div class="col col-12 col-md-7">
{% include 'inc/sync_warning.html' %}
<div class="card">
{% include 'extras/inc/configcontext_data.html' with title="Data" data=object.data format=format copyid="data" %}
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,39 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load static %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-6">
<div class="card">
<h2 class="card-header">{% trans "Config Context Profile" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
{% include 'core/inc/datafile_panel.html' %}
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% include 'inc/panels/comments.html' %}
</div>
<div class="col col-6">
<div class="card">
<h2 class="card-header d-flex justify-content-between">
{% trans "JSON Schema" %}
<div>
{% copy_content "schema" %}
</div>
</h2>
<pre class="card-body rendered-context-data m-0" id="schema">{{ object.schema|json }}</pre>
</div>
</div>
</div>
{% endblock %}

View File

@@ -1,100 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Config Template" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "MIME Type" %}</th>
<td>{{ object.mime_type|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "File Name" %}</th>
<td>{{ object.file_name|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "File Extension" %}</th>
<td>{{ object.file_extension|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Attachment" %}</th>
<td>{% checkmark object.as_attachment %}</td>
</tr>
<tr>
<th scope="row">{% trans "Data Source" %}</th>
<td>
{% if object.data_source %}
<a href="{{ object.data_source.get_absolute_url }}">{{ object.data_source }}</a>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Data File" %}</th>
<td>
{% if object.data_file %}
<a href="{{ object.data_file.get_absolute_url }}">{{ object.data_file }}</a>
{% elif object.data_path %}
<div class="float-end text-warning">
<i class="mdi mdi-alert" title="{% trans "The data file associated with this object has been deleted" %}."></i>
</div>
{{ object.data_path }}
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Data Synced" %}</th>
<td>{{ object.data_synced|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Auto Sync Enabled" %}</th>
<td>{% checkmark object.auto_sync_enabled %}</td>
</tr>
</table>
</div>
{% include 'inc/panels/tags.html' %}
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Environment Parameters" %}</h2>
<div class="card-body">
{% if object.environment_params %}
<pre>{{ object.environment_params|json }}</pre>
{% else %}
<span class="text-muted">{% trans "None" %}</span>
{% endif %}
</div>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div class="card">
<h2 class="card-header">{% trans "Template" %}</h2>
<div class="card-body">
{% include 'inc/sync_warning.html' %}
<pre>{{ object.template_code }}</pre>
</div>
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,9 @@
{% load i18n %}
{% if object.data_file %}
<a href="{{ object.data_file.get_absolute_url }}">{{ object.data_file }}</a>
{% else %}
<div class="float-end text-warning">
<i class="mdi mdi-alert" title="{% trans "The data file associated with this object has been deleted" %}."></i>
</div>
{{ value }}
{% endif %}

View File

@@ -1,163 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Custom Field" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">Type</th>
<td>
{{ object.get_type_display }}
{% if object.related_object_type %}
({{ object.related_object_type.model|bettertitle }})
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Label" %}</th>
<td>{{ object.label|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Group Name" %}</th>
<td>{{ object.group_name|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|markdown|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Required" %}</th>
<td>{% checkmark object.required %}</td>
</tr>
<tr>
<th scope="row">{% trans "Must be Unique" %}</th>
<td>{% checkmark object.unique %}</td>
</tr>
<tr>
<th scope="row">{% trans "Cloneable" %}</th>
<td>{% checkmark object.is_cloneable %}</td>
</tr>
{% if object.choice_set %}
<tr>
<th scope="row">{% trans "Choice Set" %}</th>
<td>{{ object.choice_set|linkify }} ({{ object.choice_set.choices|length }} choices)</td>
</tr>
{% endif %}
<tr>
<th scope="row">{% trans "Default Value" %}</th>
<td>{{ object.default }}</td>
</tr>
<tr>
<th scope="row">{% trans "Related object filter" %}</th>
{% if object.related_object_filter %}
<td><pre>{{ object.related_object_filter|json }}</pre></td>
{% else %}
<td>{{ ''|placeholder }}</td>
{% endif %}
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Behavior" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Search Weight" %}</th>
<td>
{% if object.search_weight %}
{{ object.search_weight }}
{% else %}
<span class="text-muted">{% trans "Disabled" %}</span>
{% endif %}
</td>
</tr>
<tr>
<th scope="row">{% trans "Filter Logic" %}</th>
<td>{{ object.get_filter_logic_display }}</td>
</tr>
<tr>
<th scope="row">{% trans "Display Weight" %}</th>
<td>{{ object.weight }}</td>
</tr>
<tr>
<th scope="row">{% trans "UI Visible" %}</th>
<td>{{ object.get_ui_visible_display }}</td>
</tr>
<tr>
<th scope="row">{% trans "UI Editable" %}</th>
<td>{{ object.get_ui_editable_display }}</td>
</tr>
</table>
</div>
{% include 'inc/panels/comments.html' %}
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Object Types" %}</h2>
<table class="table table-hover attr-table">
{% for ct in object.object_types.all %}
<tr>
<td>{{ ct }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Validation Rules" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Minimum Value" %}</th>
<td>{{ object.validation_minimum|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Maximum Value" %}</th>
<td>{{ object.validation_maximum|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Regular Expression" %}</th>
<td>
{% if object.validation_regex %}
<code>{{ object.validation_regex }}</code>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">Related Objects</h2>
<ul class="list-group list-group-flush" role="presentation">
{% for qs in related_models %}
<a class="list-group-item list-group-item-action d-flex justify-content-between">
{{ qs.model|meta:"verbose_name_plural"|bettertitle }}
{% with count=qs.count %}
{% if count %}
<span class="badge text-bg-primary rounded-pill">{{ count }}</span>
{% else %}
<span class="badge text-bg-light rounded-pill">&mdash;</span>
{% endif %}
{% endwith %}
</a>
{% endfor %}
</ul>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1 @@
{% load helpers i18n %}{{ value|linkify }} ({{ value.choices|length }} {% trans "choices" %})

View File

@@ -0,0 +1 @@
{% load helpers %}<pre>{{ value|json }}</pre>

View File

@@ -0,0 +1 @@
{% load i18n %}{% if value %}{{ value }}{% else %}<span class="text-muted">{% trans "Disabled" %}</span>{% endif %}

View File

@@ -0,0 +1 @@
{% load helpers %}{{ value }}{% if object.related_object_type %} ({{ object.related_object_type.model|bettertitle }}){% endif %}

View File

@@ -1,72 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">Custom Field Choice Set</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">Name</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">Description</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">Base Choices</th>
<td>{{ object.get_base_choices_display|placeholder }}</td>
</tr>
<tr>
<th scope="row">Choices</th>
<td>{{ object.choices|length }}</td>
</tr>
<tr>
<th scope="row">Order Alphabetically</th>
<td>{% checkmark object.order_alphabetically %}</td>
</tr>
<tr>
<th scope="row">Used by</th>
<td>
<ul class="list-unstyled mb-0">
{% for cf in object.choices_for.all %}
<li>{{ cf|linkify }}</li>
{% endfor %}
</ul>
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">Choices ({{ object.choices|length }})</h2>
<table class="table table-hover">
<thead>
<tr>
<th>Value</th>
<th>Label</th>
</tr>
</thead>
{% for value, label in choices %}
<tr>
<td>{{ value }}</td>
<td>{{ label }}</td>
</tr>
{% endfor %}
</table>
{% include 'inc/paginator.html' with page=choices %}
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,71 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-5">
<div class="card">
<h2 class="card-header">{% trans "Custom Link" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Enabled" %}</th>
<td>{% checkmark object.enabled %}</td>
</tr>
<tr>
<th scope="row">{% trans "Group Name" %}</th>
<td>{{ object.group_name|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Weight" %}</th>
<td>{{ object.weight }}</td>
</tr>
<tr>
<th scope="row">{% trans "Button Class" %}</th>
<td>{{ object.get_button_class_display }}</td>
</tr>
<tr>
<th scope="row">{% trans "New Window" %}</th>
<td>{% checkmark object.new_window %}</td>
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Assigned Models" %}</h2>
<table class="table table-hover attr-table">
{% for ct in object.object_types.all %}
<tr>
<td>{{ ct }}</td>
</tr>
{% endfor %}
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-7">
<div class="card">
<h2 class="card-header">{% trans "Link Text" %}</h2>
<div class="card-body">
<pre>{{ object.link_text }}</pre>
</div>
</div>
<div class="card">
<h2 class="card-header">{% trans "Link URL" %}</h2>
<div class="card-body">
<pre>{{ object.link_url }}</pre>
</div>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,105 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Event Rule" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Enabled" %}</th>
<td>{% checkmark object.enabled %}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Object Types" %}</h2>
<table class="table table-hover attr-table">
{% for object_type in object.object_types.all %}
<tr>
<td>{{ object_type }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Event Types" %}</h2>
<ul class="list-group list-group-flush">
{% for name, event in registry.event_types.items %}
<li class="list-group-item">
<div class="row align-items-center">
<div class="col-auto">
{% if name in object.event_types %}
{% checkmark True %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</div>
<div class="col">
{{ event }}
</div>
</div>
</li>
{% endfor %}
</ul>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Conditions" %}</h2>
<div class="card-body">
{% if object.conditions %}
<pre>{{ object.conditions|json }}</pre>
{% else %}
<p class="text-muted">{% trans "None" %}</p>
{% endif %}
</div>
</div>
<div class="card">
<h2 class="card-header">{% trans "Action" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Type" %}</th>
<td>{{ object.get_action_type_display }}</td>
</tr>
<tr>
<th scope="row">{% trans "Object" %}</th>
<td>
{{ object.action_object|linkify }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Data" %}</th>
<td>
{% if object.action_data %}
<pre>{{ object.action_data|json }}</pre>
{% else %}
{{ ''|placeholder }}
{% endif %}
</td>
</tr>
</table>
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1 @@
{% load helpers %}{% if value %}<pre>{{ value|json }}</pre>{% else %}<span class="text-muted">&mdash;</span>{% endif %}

View File

@@ -1,79 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block title %}{{ object.name }}{% endblock %}
{% block content %}
<div class="row">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Export Template" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "MIME Type" %}</th>
<td>{{ object.mime_type|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "File Name" %}</th>
<td>{{ object.file_name|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "File Extension" %}</th>
<td>{{ object.file_extension|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Attachment" %}</th>
<td>{% checkmark object.as_attachment %}</td>
</tr>
</table>
</div>
{% include 'core/inc/datafile_panel.html' %}
{% plugin_left_page object %}
</div>
<div class="col col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Assigned Models" %}</h2>
<table class="table table-hover attr-table">
{% for object_type in object.object_types.all %}
<tr>
<td>{{ object_type }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Environment Parameters" %}</h2>
<div class="card-body">
{% if object.environment_params %}
<pre>{{ object.environment_params|json }}</pre>
{% else %}
<span class="text-muted">{% trans "None" %}</span>
{% endif %}
</div>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div class="card">
<h2 class="card-header">{% trans "Template" %}</h2>
<div class="card-body">
{% include 'inc/sync_warning.html' %}
<pre>{{ object.template_code }}</pre>
</div>
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,67 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Image Attachment" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Parent Object" %}</th>
<td>{{ object.parent|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "File" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Filename" %}</th>
<td>
<a href="{{ object.image.url }}" target="_blank">{{ object.filename }}</a>
<i class="mdi mdi-open-in-new"></i>
</td>
</tr>
<tr>
<th scope="row">{% trans "Dimensions" %}</th>
<td>{{ object.image_width }} × {{ object.image_height }}</td>
</tr>
<tr>
<th scope="row">{% trans "Size" %}</th>
<td>
<span title="{{ object.size }} {% trans "bytes" %}">{{ object.size|filesizeformat }}</span>
</td>
</tr>
</table>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div class="card">
<h2 class="card-header">{% trans "Image" %}</h2>
<div class="card-body">
<a href="{{ object.image.url }}" title="{{ object.name }}">
{{ object.html_tag }}
</a>
</div>
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,42 +1,6 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load static %}
{% load i18n %}
{% block breadcrumbs %} {% block breadcrumbs %}
{{ block.super }} {{ block.super }}
<li class="breadcrumb-item"><a href="{% action_url object.assigned_object 'journal' pk=object.assigned_object.pk %}">{{ object.assigned_object }}</a></li> <li class="breadcrumb-item"><a href="{% action_url object.assigned_object 'journal' pk=object.assigned_object.pk %}">{{ object.assigned_object }}</a></li>
{% endblock %} {% endblock %}
{% block content %}
<div class="row mb-3">
<div class="col col-md-5">
<div class="card">
<h2 class="card-header">{% trans "Journal Entry" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Object" %}</th>
<td>{{ object.assigned_object|linkify }}</td>
</tr>
<tr>
<th scope="row">{% trans "Created" %}</th>
<td>{{ object.created|isodatetime:"minutes" }}</td>
</tr>
<tr>
<th scope="row">{% trans "Created By" %}</th>
<td>{{ object.created_by }}</td>
</tr>
<tr>
<th scope="row">{% trans "Kind" %}</th>
<td>{% badge object.get_kind_display bg_color=object.get_kind_color %}</td>
</tr>
</table>
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
</div>
<div class="col col-md-7">
{% include 'inc/panels/comments.html' %}
</div>
</div>
{% endblock %}

View File

@@ -1,57 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Notification Group" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>
{{ object.name }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>
{{ object.description|placeholder }}
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Groups" %}</h2>
<div class="list-group list-group-flush">
{% for group in object.groups.all %}
<a href="{{ group.get_absolute_url }}" class="list-group-item list-group-item-action">{{ group }}</a>
{% empty %}
<div class="list-group-item text-muted">{% trans "None assigned" %}</div>
{% endfor %}
</div>
</div>
<div class="card">
<h2 class="card-header">{% trans "Users" %}</h2>
<div class="list-group list-group-flush">
{% for user in object.users.all %}
<a href="{{ user.get_absolute_url }}" class="list-group-item list-group-item-action">{{ user }}</a>
{% empty %}
<div class="list-group-item text-muted">{% trans "None assigned" %}</div>
{% endfor %}
</div>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -0,0 +1,21 @@
{% extends "ui/panels/_base.html" %}
{% load helpers i18n %}
{% block panel_content %}
<table class="table table-hover attr-table">
{% for title, objects in assigned_objects %}
<tr>
<th scope="row">{{ title }}</th>
<td>
<ul class="list-unstyled mb-0">
{% for obj in objects %}
<li>{{ obj|linkify }}</li>
{% empty %}
<li class="text-muted">{% trans "None" %}</li>
{% endfor %}
</ul>
</td>
</tr>
{% endfor %}
</table>
{% endblock panel_content %}

View File

@@ -0,0 +1,5 @@
{% load helpers i18n %}
{% include 'inc/sync_warning.html' %}
<div class="card">
{% include 'extras/inc/configcontext_data.html' with title="Data" data=object.data format=format copyid="data" %}
</div>

View File

@@ -0,0 +1,6 @@
{% extends "ui/panels/_base.html" %}
{% load helpers %}
{% block panel_content %}
<pre class="card-body rendered-context-data m-0" id="schema">{{ object.schema|json }}</pre>
{% endblock panel_content %}

View File

@@ -0,0 +1,21 @@
{% extends "ui/panels/_base.html" %}
{% load helpers i18n %}
{% block panel_content %}
<ul class="list-group list-group-flush" role="presentation">
{% for qs in related_models %}
<a class="list-group-item list-group-item-action d-flex justify-content-between">
{{ qs.model|meta:"verbose_name_plural"|bettertitle }}
{% with count=qs.count %}
{% if count %}
<span class="badge text-bg-primary rounded-pill">{{ count }}</span>
{% else %}
<span class="badge text-bg-light rounded-pill">&mdash;</span>
{% endif %}
{% endwith %}
</a>
{% empty %}
<span class="list-group-item text-muted">{% trans "None" %}</span>
{% endfor %}
</ul>
{% endblock panel_content %}

View File

@@ -0,0 +1,22 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<table class="table table-hover">
<thead>
<tr>
<th>{% trans "Value" %}</th>
<th>{% trans "Label" %}</th>
</tr>
</thead>
<tbody>
{% for value, label in choices %}
<tr>
<td>{{ value }}</td>
<td>{{ label }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% include 'inc/paginator.html' with page=choices %}
{% endblock panel_content %}

View File

@@ -0,0 +1,23 @@
{% extends "ui/panels/_base.html" %}
{% load helpers %}
{% block panel_content %}
<ul class="list-group list-group-flush">
{% for name, event in registry.event_types.items %}
<li class="list-group-item">
<div class="row align-items-center">
<div class="col-auto">
{% if name in object.event_types %}
{% checkmark True %}
{% else %}
{{ ''|placeholder }}
{% endif %}
</div>
<div class="col">
{{ event }}
</div>
</div>
</li>
{% endfor %}
</ul>
{% endblock panel_content %}

View File

@@ -0,0 +1,24 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Filename" %}</th>
<td>
<a href="{{ object.image.url }}" target="_blank">{{ object.filename }}</a>
<i class="mdi mdi-open-in-new"></i>
</td>
</tr>
<tr>
<th scope="row">{% trans "Dimensions" %}</th>
<td>{{ object.image_width }} &times; {{ object.image_height }}</td>
</tr>
<tr>
<th scope="row">{% trans "Size" %}</th>
<td>
<span title="{{ object.size }} {% trans "bytes" %}">{{ object.size|filesizeformat }}</span>
</td>
</tr>
</table>
{% endblock panel_content %}

View File

@@ -0,0 +1,9 @@
{% extends "ui/panels/_base.html" %}
{% block panel_content %}
<div class="card-body">
<a href="{{ object.image.url }}" title="{{ object.name }}">
{{ object.html_tag }}
</a>
</div>
{% endblock panel_content %}

View File

@@ -0,0 +1,12 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<div class="list-group list-group-flush">
{% for group in object.groups.all %}
<a href="{{ group.get_absolute_url }}" class="list-group-item list-group-item-action">{{ group }}</a>
{% empty %}
<div class="list-group-item text-muted">{% trans "None assigned" %}</div>
{% endfor %}
</div>
{% endblock panel_content %}

View File

@@ -0,0 +1,12 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<div class="list-group list-group-flush">
{% for user in object.users.all %}
<a href="{{ user.get_absolute_url }}" class="list-group-item list-group-item-action">{{ user }}</a>
{% empty %}
<div class="list-group-item text-muted">{% trans "None assigned" %}</div>
{% endfor %}
</div>
{% endblock panel_content %}

View File

@@ -0,0 +1,11 @@
{% extends "ui/panels/_base.html" %}
{% block panel_content %}
<table class="table table-hover attr-table">
{% for ct in object.object_types.all %}
<tr>
<td>{{ ct }}</td>
</tr>
{% endfor %}
</table>
{% endblock panel_content %}

View File

@@ -0,0 +1,16 @@
{% extends "ui/panels/_base.html" %}
{% load helpers i18n %}
{% block panel_content %}
<div class="list-group list-group-flush" role="presentation">
{% for object_type in object.object_types.all %}
{% with object_type.model_class|validated_viewname:"list" as viewname %}
{% if viewname %}
<a href="{% url viewname %}?{{ object.url_params }}" class="list-group-item list-group-item-action">{{ object_type }}</a>
{% else %}
<div class="list-group-item list-group-item-action">{{ object_type }}</div>
{% endif %}
{% endwith %}
{% endfor %}
</div>
{% endblock panel_content %}

View File

@@ -0,0 +1,8 @@
{% extends "ui/panels/_base.html" %}
{% block panel_content %}
<div class="card-body">
{% include 'inc/sync_warning.html' %}
<pre>{{ value }}</pre>
</div>
{% endblock panel_content %}

View File

@@ -0,0 +1,14 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<ul class="list-group list-group-flush" role="presentation">
{% for name in object.columns %}
<li class="list-group-item list-group-item-action">
{% with column=columns|get_key:name %}
{{ column.verbose_name }}
{% endwith %}
</li>
{% endfor %}
</ul>
{% endblock panel_content %}

View File

@@ -0,0 +1,21 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<ul class="list-group list-group-flush" role="presentation">
{% for column_name, ascending in object.ordering_items %}
<li class="list-group-item">
{% with column=columns|get_key:column_name %}
{% if ascending %}
<i class="mdi mdi-arrow-down-thick"></i>
{% else %}
<i class="mdi mdi-arrow-up-thick"></i>
{% endif %}
{{ column.verbose_name }}
{% endwith %}
</li>
{% empty %}
<li class="list-group-item text-muted">{% trans "Default" %}</li>
{% endfor %}
</ul>
{% endblock panel_content %}

View File

@@ -0,0 +1,21 @@
{% extends "ui/panels/_base.html" %}
{% load helpers i18n %}
{% block panel_content %}
<ul class="list-group list-group-flush" role="presentation">
{% for object_type in object_types %}
{% action_url object_type.content_type.model_class 'list' as list_url %}
{% if list_url %}
<a href="{{ list_url }}?tag={{ object.slug }}" class="list-group-item list-group-item-action d-flex justify-content-between">
{{ object_type.content_type.name|bettertitle }}
<span class="badge text-bg-primary rounded-pill">{{ object_type.item_count }}</span>
</a>
{% else %}
<li class="list-group-item list-group-item-action d-flex justify-content-between">
{{ object_type.content_type.name|bettertitle }}
<span class="badge text-bg-primary rounded-pill">{{ object_type.item_count }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
{% endblock panel_content %}

View File

@@ -0,0 +1,16 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<table class="table table-hover attr-table">
{% for ct in object.object_types.all %}
<tr>
<td>{{ ct }}</td>
</tr>
{% empty %}
<tr>
<td class="text-muted">{% trans "Any" %}</td>
</tr>
{% endfor %}
</table>
{% endblock panel_content %}

View File

@@ -0,0 +1,12 @@
{% extends "ui/panels/_base.html" %}
{% load i18n %}
{% block panel_content %}
<div class="card-body">
{% if value %}
<pre>{{ value }}</pre>
{% else %}
<span class="text-muted">{% trans "None" %}</span>
{% endif %}
</div>
{% endblock panel_content %}

View File

@@ -1,69 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-5">
<div class="card">
<h2 class="card-header">{% trans "Saved Filter" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "User" %}</th>
<td>{{ object.user|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Enabled" %}</th>
<td>{% checkmark object.enabled %}</td>
</tr>
<tr>
<th scope="row">{% trans "Shared" %}</th>
<td>{% checkmark object.shared %}</td>
</tr>
<tr>
<th scope="row">{% trans "Weight" %}</th>
<td>{{ object.weight }}</td>
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Assigned Models" %}</h2>
<div class="list-group list-group-flush" role="presentation">
{% for object_type in object.object_types.all %}
{% with object_type.model_class|validated_viewname:"list" as viewname %}
{% if viewname %}
<a href="{% url viewname %}?{{ object.url_params }}" class="list-group-item list-group-item-action">{{ object_type }}</a>
{% else %}
<div class="list-group-item list-group-item-action">{{ object_type }}</div>
{% endif %}
{% endwith %}
{% endfor %}
</div>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-7">
<div class="card">
<h2 class="card-header">{% trans "Parameters" %}</h2>
<div class="card-body p-0">
<pre>{{ object.parameters|json }}</pre>
</div>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,88 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<div class="col col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Table Config" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Object Type" %}</th>
<td>{{ object.object_type }}</td>
</tr>
<tr>
<th scope="row">{% trans "Table" %}</th>
<td>{{ object.table }}</td>
</tr>
<tr>
<th scope="row">{% trans "User" %}</th>
<td>{{ object.user|placeholder }}</td>
</tr>
<tr>
<th scope="row">{% trans "Enabled" %}</th>
<td>{% checkmark object.enabled %}</td>
</tr>
<tr>
<th scope="row">{% trans "Shared" %}</th>
<td>{% checkmark object.shared %}</td>
</tr>
<tr>
<th scope="row">{% trans "Weight" %}</th>
<td>{{ object.weight }}</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Columns Displayed" %}</h2>
<ul class="list-group list-group-flush" role="presentation">
{% for name in object.columns %}
<li class="list-group-item list-group-item-action">
{% with column=columns|get_key:name %}
{{ column.verbose_name }}
{% endwith %}
</li>
{% endfor %}
</ul>
</div>
<div class="card">
<h2 class="card-header">{% trans "Ordering" %}</h2>
<ul class="list-group list-group-flush" role="presentation">
{% for column, ascending in object.ordering_items %}
<li class="list-group-item">
{% with column=columns|get_key:column %}
{% if ascending %}
<i class="mdi mdi-arrow-down-thick"></i>
{% else %}
<i class="mdi mdi-arrow-up-thick"></i>
{% endif %}
{{ column.verbose_name }}
{% endwith %}
</li>
{% empty %}
<li class="list-group-item text-muted">{% trans "Default" %}</li>
{% endfor %}
</ul>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,94 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load render_table from django_tables2 %}
{% load i18n %}
{% block content %}
<div class="row">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Tag" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>
{{ object.name }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>
{{ object.description|placeholder }}
</td>
</tr>
<tr>
<th scope="row">{% trans "Color" %}</th>
<td>
<span class="color-label" style="background-color: #{{ object.color }}">&nbsp;</span>
</td>
</tr>
<tr>
<th scope="row">{% trans "Weight" %}</th>
<td>{{ object.weight }}</td>
</tr>
<tr>
<th scope="row">{% trans "Tagged Items" %}</th>
<td>
{{ taggeditem_table.rows|length }}
</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Allowed Object Types" %}</h2>
<table class="table table-hover attr-table">
{% for ct in object.object_types.all %}
<tr>
<td>{{ ct }}</td>
</tr>
{% empty %}
<tr>
<td class="text-muted">{% trans "Any" %}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "Tagged Item Types" %}</h2>
<ul class="list-group list-group-flush" role="presentation">
{% for object_type in object_types %}
{% action_url object_type.content_type.model_class 'list' as list_url %}
{% if list_url %}
<a href="{{ list_url }}?tag={{ object.slug }}" class="list-group-item list-group-item-action d-flex justify-content-between">
{{ object_type.content_type.name|bettertitle }}
<span class="badge text-bg-primary rounded-pill">{{ object_type.item_count }}</span>
</a>
{% else %}
<li class="list-group-item list-group-item-action d-flex justify-content-between">
{{ object_type.content_type.name|bettertitle }}
<span class="badge text-bg-primary rounded-pill">{{ object_type.item_count }}</span>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
<div class="card">
<h2 class="card-header">{% trans "Tagged Objects" %}</h2>
<div class="table-responsive">
{% render_table taggeditem_table 'inc/table.html' %}
{% include 'inc/paginator.html' with paginator=taggeditem_table.paginator page=taggeditem_table.page %}
</div>
</div>
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}

View File

@@ -1,89 +1 @@
{% extends 'generic/object.html' %} {% extends 'generic/object.html' %}
{% load helpers %}
{% load plugins %}
{% load i18n %}
{% block content %}
<div class="row mb-3">
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Webhook" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "Name" %}</th>
<td>{{ object.name }}</td>
</tr>
<tr>
<th scope="row">{% trans "Description" %}</th>
<td>{{ object.description|placeholder }}</td>
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "HTTP Request" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "HTTP Method" %}</th>
<td>{{ object.get_http_method_display }}</td>
</tr>
<tr>
<th scope="row">{% trans "Payload URL" %}</th>
<td class="font-monospace">{{ object.payload_url }}</td>
</tr>
<tr>
<th scope="row">{% trans "HTTP Content Type" %}</th>
<td>{{ object.http_content_type }}</td>
</tr>
<tr>
<th scope="row">{% trans "Secret" %}</th>
<td>{{ object.secret|placeholder }}</td>
</tr>
</table>
</div>
<div class="card">
<h2 class="card-header">{% trans "SSL" %}</h2>
<table class="table table-hover attr-table">
<tr>
<th scope="row">{% trans "SSL Verification" %}</th>
<td>{% checkmark object.ssl_verification %}</td>
</tr>
<tr>
<th scope="row">{% trans "CA File Path" %}</th>
<td>{{ object.ca_file_path|placeholder }}</td>
</tr>
</table>
</div>
{% plugin_left_page object %}
</div>
<div class="col col-12 col-md-6">
<div class="card">
<h2 class="card-header">{% trans "Additional Headers" %}</h2>
<div class="card-body">
{% if object.additional_headers %}
<pre>{{ object.additional_headers }}</pre>
{% else %}
<span class="text-muted">{% trans "None" %}</span>
{% endif %}
</div>
</div>
<div class="card">
<h2 class="card-header">{% trans "Body Template" %}</h2>
<div class="card-body">
{% if object.body_template %}
<pre>{{ object.body_template }}</pre>
{% else %}
<span class="text-muted">{% trans "None" %}</span>
{% endif %}
</div>
</div>
{% include 'inc/panels/custom_fields.html' %}
{% include 'inc/panels/tags.html' %}
{% plugin_right_page object %}
</div>
</div>
<div class="row">
<div class="col col-md-12">
{% plugin_full_width_page object %}
</div>
</div>
{% endblock %}