-
-
Notifications
You must be signed in to change notification settings - Fork 76
Review admin template structure #475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
9b7a6e5
9b1e592
414522f
a028204
57eacf6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,90 +1 @@ | ||
| {% extends "admin/base_site.html" %} | ||
| {% load i18n static notification_tags %} | ||
|
|
||
| {% block extrastyle %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget %} | ||
| <link rel="stylesheet" type="text/css" href="{% static 'openwisp-notifications/css/loader.css' %}" /> | ||
| <link rel="stylesheet" type="text/css" href="{% static 'openwisp-notifications/css/notifications.css' %}" /> | ||
| {% endif %} | ||
| {% endblock extrastyle %} | ||
|
|
||
| {% block extrahead %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget and not media.js and 'jquery' not in block.super %} | ||
| <script src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script> | ||
| <script src="{% static 'admin/js/jquery.init.js' %}"></script> | ||
| {% endif %} | ||
| {% endblock %} | ||
|
|
||
| {% block user-tools %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget %} | ||
| <button class="ow-notifications toggle-btn" id="openwisp_notifications"> | ||
| <span id="ow-notification-btn"></span> | ||
| {% unread_notifications %} | ||
| </button> | ||
| {% csrf_token %} | ||
| <div class="ow-notification-toast-wrapper"></div> | ||
| <div class="ow-notification-dropdown ow-hide"> | ||
| <div class="filters"> | ||
| <span class="toggle-btn" id="ow-mark-all-read" tabindex="0" role="button"> | ||
| {% trans 'Mark all as read' %} | ||
| </span> | ||
| <a class="toggle-btn" href="{% url 'notifications:notification_preference' %}"> | ||
| {% trans 'Notification Preferences' %} | ||
| </a> | ||
| </div> | ||
| <div class="ow-notification-wrapper ow-round-bottom-border"> | ||
| <div id="ow-notifications-loader" class="ow-hide"><div class="loader"></div></div> | ||
| </div> | ||
| <div class="ow-no-notifications ow-round-bottom-border ow-hide"> | ||
| <p>{% trans 'No new notification.' %}</p> | ||
| </div> | ||
| </div> | ||
| {% endif %} | ||
| {% endblock %} | ||
|
|
||
| {% block footer %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget %} | ||
| <div class="ow-overlay ow-overlay-notification ow-overlay-inner ow-hide"> | ||
| <div class="ow-dialog-notification"> | ||
| <span class="ow-dialog-close ow-dialog-close-x">×</span> | ||
| <div class="ow-notification-level-wrapper ow-dialog-notification-level-wrapper"></div> | ||
| <h2 class="ow-message-title"></h2> | ||
| <div class="ow-message-description"></div> | ||
| <div class="ow-dialog-buttons"> | ||
| <button class="ow-message-target-redirect ow-hide button default success-btn"> | ||
| {% trans 'Open' %} | ||
| </button> | ||
| <button class="ow-dialog-close button default"> | ||
| {% trans 'Close' %} | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <script type="text/javascript" src="{% static 'openwisp-notifications/js/vendor/reconnecting-websocket.min.js' %}"></script> | ||
| <script type="text/javascript"> | ||
| {% if OPENWISP_NOTIFICATIONS_HOST %} | ||
| const notificationApiHost = new URL('{{ OPENWISP_NOTIFICATIONS_HOST }}'); | ||
| {% else %} | ||
| const notificationApiHost = window.location; | ||
| {% endif %} | ||
| const webSocketProtocol = notificationApiHost.protocol === 'http:' ? 'ws' : 'wss'; | ||
| const notificationSound = new Audio('{{ OPENWISP_NOTIFICATIONS_SOUND | default:"" }}'); | ||
| // Create websocket connection | ||
| const notificationSocket = new ReconnectingWebSocket( | ||
| `${webSocketProtocol}://${notificationApiHost.host}/ws/notification/`, | ||
| null, { | ||
| debug: false, | ||
| // The library re-connects if it fails to establish a connection in "timeoutInterval". | ||
| // On slow internet connections, the default value of "timeoutInterval" will | ||
| // keep terminating and re-establishing the connection. | ||
| timeoutInterval: 7000, | ||
| } | ||
| ); | ||
| </script> | ||
| <script type="text/javascript" src="{% static 'openwisp-notifications/js/notifications.js' %}"></script> | ||
| {% endif %} | ||
| {% endblock footer %} | ||
| {% extends 'admin/openwisp_notifications/base_site.html' %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,35 +1 @@ | ||
| {% extends 'admin/change_form.html' %} | ||
|
|
||
| {% comment %} | ||
| This template is required to supply information regarding the displayed object | ||
| from Django's template context to JavaScript for proper functioning of object notification | ||
| widget. Following data is supplied: | ||
| 1. object's app_label | ||
| 2. object's model_name | ||
| 3. object's primary key | ||
| {% endcomment %} | ||
|
|
||
| {% block admin_change_form_document_ready %} | ||
| {{ block.super }} | ||
| {% if change %} | ||
| <script> | ||
| const owNotifyAppLabel = '{{ opts.app_label }}', | ||
| owNotifyModelName = '{{ opts.model_name }}', | ||
| uuidRegex = /\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i, | ||
| owIsChangeForm = true; | ||
| let objectId; | ||
| try{ | ||
| objectId = uuidRegex.exec(window.location.pathname)[0]; | ||
| } | ||
| catch(error){ | ||
| try{ | ||
| objectId = /\/(\d+)\//.exec(window.location.pathname)[0]; | ||
| } | ||
| catch(error){ | ||
| throw error; | ||
| } | ||
| } | ||
| const owNotifyObjectId = objectId.replace(/\//g,''); | ||
| </script> | ||
| {% endif %} | ||
| {% endblock %} | ||
| {% extends 'admin/openwisp_notifications/change_form.html' %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,90 @@ | ||
| {% extends "admin/base_site.html" %} | ||
| {% load i18n static notification_tags %} | ||
|
|
||
| {% block extrastyle %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget %} | ||
| <link rel="stylesheet" type="text/css" href="{% static 'openwisp-notifications/css/loader.css' %}" /> | ||
| <link rel="stylesheet" type="text/css" href="{% static 'openwisp-notifications/css/notifications.css' %}" /> | ||
| {% endif %} | ||
| {% endblock extrastyle %} | ||
|
|
||
| {% block extrahead %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget and not media.js and 'jquery' not in block.super %} | ||
| <script src="{% static 'admin/js/vendor/jquery/jquery.min.js' %}"></script> | ||
| <script src="{% static 'admin/js/jquery.init.js' %}"></script> | ||
| {% endif %} | ||
| {% endblock %} | ||
|
|
||
| {% block user-tools %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget %} | ||
| <button class="ow-notifications toggle-btn" id="openwisp_notifications"> | ||
| <span id="ow-notification-btn"></span> | ||
| {% unread_notifications %} | ||
| </button> | ||
| {% csrf_token %} | ||
| <div class="ow-notification-toast-wrapper"></div> | ||
| <div class="ow-notification-dropdown ow-hide"> | ||
| <div class="filters"> | ||
| <span class="toggle-btn" id="ow-mark-all-read" tabindex="0" role="button"> | ||
| {% trans 'Mark all as read' %} | ||
| </span> | ||
| <a class="toggle-btn" href="{% url 'notifications:notification_preference' %}"> | ||
| {% trans 'Notification Preferences' %} | ||
| </a> | ||
| </div> | ||
| <div class="ow-notification-wrapper ow-round-bottom-border"> | ||
| <div id="ow-notifications-loader" class="ow-hide"><div class="loader"></div></div> | ||
| </div> | ||
| <div class="ow-no-notifications ow-round-bottom-border ow-hide"> | ||
| <p>{% trans 'No new notification.' %}</p> | ||
| </div> | ||
| </div> | ||
| {% endif %} | ||
| {% endblock %} | ||
|
|
||
| {% block footer %} | ||
| {{ block.super }} | ||
| {% if request|should_load_notifications_widget %} | ||
| <div class="ow-overlay ow-overlay-notification ow-overlay-inner ow-hide"> | ||
| <div class="ow-dialog-notification"> | ||
| <span class="ow-dialog-close ow-dialog-close-x">×</span> | ||
| <div class="ow-notification-level-wrapper ow-dialog-notification-level-wrapper"></div> | ||
| <h2 class="ow-message-title"></h2> | ||
| <div class="ow-message-description"></div> | ||
| <div class="ow-dialog-buttons"> | ||
| <button class="ow-message-target-redirect ow-hide button default success-btn"> | ||
| {% trans 'Open' %} | ||
| </button> | ||
| <button class="ow-dialog-close button default"> | ||
| {% trans 'Close' %} | ||
| </button> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <script type="text/javascript" src="{% static 'openwisp-notifications/js/vendor/reconnecting-websocket.min.js' %}"></script> | ||
| <script type="text/javascript"> | ||
| {% if OPENWISP_NOTIFICATIONS_HOST %} | ||
| const notificationApiHost = new URL('{{ OPENWISP_NOTIFICATIONS_HOST }}'); | ||
| {% else %} | ||
| const notificationApiHost = window.location; | ||
| {% endif %} | ||
| const webSocketProtocol = notificationApiHost.protocol === 'http:' ? 'ws' : 'wss'; | ||
| const notificationSound = new Audio('{{ OPENWISP_NOTIFICATIONS_SOUND | default:"" }}'); | ||
| // Create websocket connection | ||
| const notificationSocket = new ReconnectingWebSocket( | ||
| `${webSocketProtocol}://${notificationApiHost.host}/ws/notification/`, | ||
| null, { | ||
| debug: false, | ||
| // The library re-connects if it fails to establish a connection in "timeoutInterval". | ||
| // On slow internet connections, the default value of "timeoutInterval" will | ||
| // keep terminating and re-establishing the connection. | ||
| timeoutInterval: 7000, | ||
| } | ||
| ); | ||
| </script> | ||
| <script type="text/javascript" src="{% static 'openwisp-notifications/js/notifications.js' %}"></script> | ||
| {% endif %} | ||
| {% endblock footer %} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| {% extends 'admin/change_form.html' %} | ||
|
|
||
| {% comment %} | ||
| This template is required to supply information regarding the displayed object | ||
| from Django's template context to JavaScript for proper functioning of object notification | ||
| widget. Following data is supplied: | ||
| 1. object's app_label | ||
| 2. object's model_name | ||
| 3. object's primary key | ||
| {% endcomment %} | ||
|
Comment on lines
+3
to
+10
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Stop deriving This hard-codes UUID and integer admin URLs, then throws on anything else. A model with a string PK or a custom admin path will break this bootstrap before Use Django’s server-side object id in the template and drop the regex/ Suggested direction {% block admin_change_form_document_ready %}
{{ block.super }}
{% if change %}
<script>
- const owNotifyAppLabel = '{{ opts.app_label }}',
- owNotifyModelName = '{{ opts.model_name }}',
- uuidRegex = /\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i,
- owIsChangeForm = true;
- let objectId;
- try{
- objectId = uuidRegex.exec(window.location.pathname)[0];
- }
- catch(error){
- try{
- objectId = /\/(\d+)\//.exec(window.location.pathname)[0];
- }
- catch(error){
- throw error;
- }
- }
- const owNotifyObjectId = objectId.replace(/\//g,'');
+ const owNotifyAppLabel = '{{ opts.app_label|escapejs }}';
+ const owNotifyModelName = '{{ opts.model_name|escapejs }}';
+ const owNotifyObjectId = '{{ object_id|escapejs }}';
+ const owIsChangeForm = true;
</script>
{% endif %}
{% endblock %}As per coding guidelines, "Flag outdated or incorrect documentation, comments, or docstrings" and "Cryptic or non-obvious code (regex, complex bash commands, or hard-to-read code) must include a concise comment explaining why it is needed and why the complexity is acceptable". Also applies to: 16-32 🤖 Prompt for AI Agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not the point of this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Would you like me to open a follow-up issue to track the concern about replacing the client-side (ᵔ ᴥ ᵔ) 🧠 Learnings used |
||
|
|
||
| {% block admin_change_form_document_ready %} | ||
| {{ block.super }} | ||
| {% if change %} | ||
| <script> | ||
| const owNotifyAppLabel = '{{ opts.app_label }}', | ||
| owNotifyModelName = '{{ opts.model_name }}', | ||
| uuidRegex = /\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i, | ||
| owIsChangeForm = true; | ||
| let objectId; | ||
| try{ | ||
| objectId = uuidRegex.exec(window.location.pathname)[0]; | ||
| } | ||
| catch(error){ | ||
| try{ | ||
| objectId = /\/(\d+)\//.exec(window.location.pathname)[0]; | ||
| } | ||
| catch(error){ | ||
| throw error; | ||
| } | ||
| } | ||
| const owNotifyObjectId = objectId.replace(/\//g,''); | ||
| </script> | ||
| {% endif %} | ||
| {% endblock %} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| {% extends 'admin/openwisp_notifications/base_site.html' %} | ||
| {% block user-tools %} | ||
| {{ block.super }} | ||
| <div id="sample-notifications-base-site"></div> | ||
| {% endblock %} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| {% extends 'admin/openwisp_notifications/change_form.html' %} | ||
| {% block admin_change_form_document_ready %} | ||
| {{ block.super }} | ||
| <div id="sample-notifications-change-form"></div> | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| {% endblock %} | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Set an explicit
type="button"on these controls.These buttons are not submit actions, so relying on the HTML default can trigger accidental form submission if this block is rendered inside or near a form later.
Suggested fix
Also applies to: 58-61
🧰 Tools
🪛 HTMLHint (1.9.2)
[warning] 23-23: The type attribute must be present on elements.
(button-type-require)
🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not the point of this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asmodehn, understood — I'll drop the suggestion for this PR. Would you like me to open a follow-up issue to track adding explicittype="button"attributes to the notification button elements inbase_site.html?(ᵕ˃ᴗ˂ᵕ)
🧠 Learnings used