From 85fd23261465e4717cb389374a4eac788f7a4fbe Mon Sep 17 00:00:00 2001
From: Arthur Hanson <worldnomad@gmail.com>
Date: Fri, 21 Jun 2024 07:04:52 -0700
Subject: [PATCH] 16149 add (optional) obj hyperlink to script list table
 (#16271)

* 16149 add (optional) obj hyperlink to script list table

* 16149 add (optional) obj hyperlink to script list table

* 16149 review feedback

* 16149 review changes
---
 docs/customization/custom-scripts.md       | 10 +++++-----
 netbox/extras/scripts.py                   |  2 ++
 netbox/extras/tables/tables.py             | 21 ++++++++++++++++++++-
 netbox/extras/views.py                     |  2 ++
 netbox/templates/extras/script_result.html |  2 +-
 5 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/docs/customization/custom-scripts.md b/docs/customization/custom-scripts.md
index 2a8f252aa2c..75463d871f5 100644
--- a/docs/customization/custom-scripts.md
+++ b/docs/customization/custom-scripts.md
@@ -138,11 +138,11 @@ These two methods will load data in YAML or JSON format, respectively, from file
 
 The Script object provides a set of convenient functions for recording messages at different severity levels:
 
-* `log_debug(message, object=None)`
-* `log_success(message, object=None)`
-* `log_info(message, object=None)`
-* `log_warning(message, object=None)`
-* `log_failure(message, object=None)`
+* `log_debug(message, obj=None)`
+* `log_success(message, obj=None)`
+* `log_info(message, obj=None)`
+* `log_warning(message, obj=None)`
+* `log_failure(message, obj=None)`
 
 Log messages are returned to the user upon execution of the script. Markdown rendering is supported for log messages. A message may optionally be associated with a particular object by passing it as the second argument to the logging method.
 
diff --git a/netbox/extras/scripts.py b/netbox/extras/scripts.py
index 0e74c3f0de3..ae27aa0fbd8 100644
--- a/netbox/extras/scripts.py
+++ b/netbox/extras/scripts.py
@@ -500,6 +500,8 @@ def _log(self, message, obj=None, level=LogLevelChoices.LOG_DEFAULT):
                 'time': timezone.now().isoformat(),
                 'status': level,
                 'message': str(message),
+                'obj': str(obj) if obj else None,
+                'url': obj.get_absolute_url() if hasattr(obj, 'get_absolute_url') else None,
             })
 
             # Record to the system log
diff --git a/netbox/extras/tables/tables.py b/netbox/extras/tables/tables.py
index 8c78ad0dec1..a120f7f53d6 100644
--- a/netbox/extras/tables/tables.py
+++ b/netbox/extras/tables/tables.py
@@ -1,6 +1,7 @@
 import json
 
 import django_tables2 as tables
+from django.utils.html import format_html
 from django.utils.translation import gettext_lazy as _
 
 from extras.models import *
@@ -545,6 +546,9 @@ class ScriptResultsTable(BaseTable):
         template_code="""{% load log_levels %}{% log_level record.status %}""",
         verbose_name=_('Level')
     )
+    object = tables.Column(
+        verbose_name=_('Object')
+    )
     message = columns.MarkdownColumn(
         verbose_name=_('Message')
     )
@@ -552,9 +556,18 @@ class ScriptResultsTable(BaseTable):
     class Meta(BaseTable.Meta):
         empty_text = _(EMPTY_TABLE_TEXT)
         fields = (
-            'index', 'time', 'status', 'message',
+            'index', 'time', 'status', 'object', 'message',
+        )
+        default_columns = (
+            'index', 'time', 'status', 'object', 'message',
         )
 
+    def render_object(self, value, record):
+        return format_html("<a href='{}'>{}</a>", record['url'], value)
+
+    def render_url(self, value):
+        return format_html("<a href='{}'>{}</a>", value, value)
+
 
 class ReportResultsTable(BaseTable):
     index = tables.Column(
@@ -585,3 +598,9 @@ class Meta(BaseTable.Meta):
         fields = (
             'index', 'method', 'time', 'status', 'object', 'url', 'message',
         )
+
+    def render_object(self, value, record):
+        return format_html("<a href='{}'>{}</a>", record['url'], value)
+
+    def render_url(self, value):
+        return format_html("<a href='{}'>{}</a>", value, value)
diff --git a/netbox/extras/views.py b/netbox/extras/views.py
index 82f519c006a..b33c1e22ac0 100644
--- a/netbox/extras/views.py
+++ b/netbox/extras/views.py
@@ -1201,6 +1201,8 @@ def get_table(self, job, request, bulk_actions=True):
                         'time': log.get('time'),
                         'status': log.get('status'),
                         'message': log.get('message'),
+                        'object': log.get('obj'),
+                        'url': log.get('url'),
                     }
                     data.append(result)
 
diff --git a/netbox/templates/extras/script_result.html b/netbox/templates/extras/script_result.html
index ccc7f4d2351..1b297673b6c 100644
--- a/netbox/templates/extras/script_result.html
+++ b/netbox/templates/extras/script_result.html
@@ -44,7 +44,7 @@
       {# Object table controls #}
       <div class="row mb-3">
         <div class="col-auto ms-auto d-print-none">
-          {% if request.user.is_authenticated %}
+          {% if request.user.is_authenticated and job.completed %}
             <div class="table-configure input-group">
               <button type="button" data-bs-toggle="modal" title="{% trans "Configure Table" %}" data-bs-target="#ObjectTable_config"
                 class="btn">