11"use strict" ;
22django . jQuery ( function ( $ ) {
3- var firstRun = true ,
3+ var pageLoading = true ,
44 backendFieldSelector = "#id_config-0-backend" ,
55 orgFieldSelector = "#id_organization" ,
66 isDeviceGroup = function ( ) {
7- return window . _deviceGroup ;
7+ return window . _deviceGroupId !== undefined ;
88 } ,
99 templatesFieldName = function ( ) {
1010 return isDeviceGroup ( ) ? "templates" : "config-0-templates" ;
1111 } ,
12+ isAddingNewObject = function ( ) {
13+ return isDeviceGroup ( )
14+ ? ! $ ( ".add-form" ) . length
15+ : $ ( 'input[name="config-0-id"]' ) . val ( ) . length === 0 ;
16+ } ,
1217 getTemplateOptionElement = function (
1318 index ,
1419 templateId ,
@@ -33,23 +38,15 @@ django.jQuery(function ($) {
3338 if ( templateConfig . required ) {
3439 inputField . prop ( "disabled" , true ) ;
3540 }
36- if ( isSelected || templateConfig . required ) {
41+ // mark the template as selected if it is required or if it is enabled for the current device or group
42+ if ( isSelected || templateConfig . required || templateConfig . selected ) {
3743 inputField . prop ( "checked" , true ) ;
3844 }
3945 return element ;
4046 } ,
4147 resetTemplateOptions = function ( ) {
4248 $ ( "ul.sortedm2m-items" ) . empty ( ) ;
4349 } ,
44- updateTemplateSelection = function ( selectedTemplates ) {
45- // Marks currently applied templates from database as selected
46- // Only executed at page load.
47- selectedTemplates . forEach ( function ( templateId ) {
48- $ (
49- `li.sortedm2m-item input[type="checkbox"][value="${ templateId } "]:first` ,
50- ) . prop ( "checked" , true ) ;
51- } ) ;
52- } ,
5350 updateTemplateHelpText = function ( ) {
5451 var helpText = "Choose items and order by drag & drop." ;
5552 if ( $ ( "li.sortedm2m-item:first" ) . length === 0 ) {
@@ -73,11 +70,32 @@ django.jQuery(function ($) {
7370 showRelevantTemplates ( ) ;
7471 } ,
7572 updateConfigTemplateField = function ( templates ) {
76- $ ( `input[name="${ templatesFieldName ( ) } "]` ) . attr (
77- "value" ,
78- templates . join ( "," ) ,
79- ) ;
80- $ ( "input.sortedm2m:first" ) . trigger ( "change" ) ;
73+ var value = templates . join ( "," ) ,
74+ templateField = templatesFieldName ( ) ,
75+ updateInitialValue = false ;
76+ $ ( `input[name="${ templateField } "]` ) . attr ( "value" , value ) ;
77+ if (
78+ pageLoading ||
79+ // Handle cases where the AJAX request finishes after initial page load.
80+ // If we're editing an existing object and the initial value hasn't been set,
81+ // assign it now to avoid false positives in the unsaved changes warning.
82+ ( ! isAddingNewObject ( ) &&
83+ django . _owcInitialValues [ templateField ] === undefined )
84+ ) {
85+ django . _owcInitialValues [ templateField ] = value ;
86+ updateInitialValue = true ;
87+ }
88+ $ ( "input.sortedm2m:first" ) . trigger ( "change" , {
89+ updateInitialValue : updateInitialValue ,
90+ } ) ;
91+ } ,
92+ getSelectedTemplates = function ( ) {
93+ // Returns the selected templates from the sortedm2m input
94+ var selectedTemplates = { } ;
95+ $ ( "input.sortedm2m:checked" ) . each ( function ( index , element ) {
96+ selectedTemplates [ $ ( element ) . val ( ) ] = $ ( element ) . prop ( "checked" ) ;
97+ } ) ;
98+ return selectedTemplates ;
8199 } ,
82100 parseSelectedTemplates = function ( selectedTemplates ) {
83101 if ( selectedTemplates !== undefined ) {
@@ -88,85 +106,57 @@ django.jQuery(function ($) {
88106 }
89107 }
90108 } ,
109+ getRelevantTemplateUrl = function ( orgID , backend ) {
110+ // Returns the URL to fetch relevant templates
111+ var baseUrl = window . _relevantTemplateUrl . replace ( "org_id" , orgID ) ;
112+ var url = new URL ( baseUrl , window . location . origin ) ;
113+
114+ // Get relevant templates of selected org and backend
115+ if ( backend ) {
116+ url . searchParams . set ( "backend" , backend ) ;
117+ }
118+ if ( isDeviceGroup ( ) && ! $ ( ".add-form" ) . length ) {
119+ url . searchParams . set ( "group_id" , window . _deviceGroupId ) ;
120+ } else if ( $ ( 'input[name="config-0-id"]' ) . length ) {
121+ url . searchParams . set ( "config_id" , $ ( 'input[name="config-0-id"]' ) . val ( ) ) ;
122+ }
123+ return url . toString ( ) ;
124+ } ,
91125 showRelevantTemplates = function ( ) {
92126 var orgID = $ ( orgFieldSelector ) . val ( ) ,
93127 backend = isDeviceGroup ( ) ? "" : $ ( backendFieldSelector ) . val ( ) ,
94- selectedTemplates ;
128+ currentSelection = getSelectedTemplates ( ) ;
95129
96130 // Hide templates if no organization or backend is selected
97- if ( orgID . length === 0 || ( ! isDeviceGroup ( ) && backend . length === 0 ) ) {
131+ if ( ! orgID || ( ! isDeviceGroup ( ) && backend . length === 0 ) ) {
98132 resetTemplateOptions ( ) ;
99133 updateTemplateHelpText ( ) ;
100134 return ;
101135 }
102136
103- if ( firstRun ) {
104- // selectedTemplates will be undefined on device add page or
105- // when the user has changed any of organization or backend field.
106- // selectedTemplates will be an empty string if no template is selected
107- // ''.split(',') returns [''] hence, this case requires special handling
108- selectedTemplates = isDeviceGroup ( )
109- ? parseSelectedTemplates ( $ ( "#id_templates" ) . val ( ) )
110- : parseSelectedTemplates (
111- django . _owcInitialValues [ templatesFieldName ( ) ] ,
112- ) ;
113- }
114-
115- var url = window . _relevantTemplateUrl . replace ( "org_id" , orgID ) ;
116- // Get relevant templates of selected org and backend
117- url = url + "?backend=" + backend ;
137+ var url = getRelevantTemplateUrl ( orgID , backend ) ;
118138 $ . get ( url ) . done ( function ( data ) {
119139 resetTemplateOptions ( ) ;
120140 var enabledTemplates = [ ] ,
121141 sortedm2mUl = $ ( "ul.sortedm2m-items:first" ) ,
122142 sortedm2mPrefixUl = $ ( "ul.sortedm2m-items:last" ) ;
123143
124- // Adds "li" elements for templates that are already selected
125- // in the database. Select these templates and remove their key from "data"
126- // This maintains the order of the templates and keep
127- // enabled templates on the top
128- if ( selectedTemplates !== undefined ) {
129- selectedTemplates . forEach ( function ( templateId , index ) {
130- // corner case in which backend of template does not match
131- if ( ! data [ templateId ] ) {
132- return ;
133- }
134- var element = getTemplateOptionElement (
135- index ,
136- templateId ,
137- data [ templateId ] ,
138- true ,
139- false ,
140- ) ,
141- prefixElement = getTemplateOptionElement (
142- index ,
143- templateId ,
144- data [ templateId ] ,
145- true ,
146- true ,
147- ) ;
148- sortedm2mUl . append ( element ) ;
149- if ( ! isDeviceGroup ( ) ) {
150- sortedm2mPrefixUl . append ( prefixElement ) ;
151- }
152- delete data [ templateId ] ;
153- } ) ;
154- }
155-
156- // Adds "li" elements for templates that are not selected
157- // in the database.
158- var counter =
159- selectedTemplates !== undefined ? selectedTemplates . length : 0 ;
144+ // Adds "li" elements for templates
160145 Object . keys ( data ) . forEach ( function ( templateId , index ) {
161- // corner case in which backend of template does not match
162- if ( ! data [ templateId ] ) {
163- return ;
164- }
165- index = index + counter ;
166146 var isSelected =
167- data [ templateId ] . default &&
168- selectedTemplates === undefined &&
169- ! data [ templateId ] . required ,
147+ // Template is selected in the database
148+ data [ templateId ] . selected ||
149+ // Shared template which was already selected
150+ ( currentSelection [ templateId ] !== undefined &&
151+ currentSelection [ templateId ] ) ||
152+ // Default template should be selected when:
153+ // 1. A new object is created.
154+ // 2. Organization or backend field has changed.
155+ // (when the fields are changed, the currentSelection will be non-empty)
156+ ( data [ templateId ] . default &&
157+ ( pageLoading ||
158+ isAddingNewObject ( ) ||
159+ Object . keys ( currentSelection ) . length > 0 ) ) ,
170160 element = getTemplateOptionElement (
171161 index ,
172162 templateId ,
@@ -180,9 +170,6 @@ django.jQuery(function ($) {
180170 isSelected ,
181171 true ,
182172 ) ;
183- // Default templates should only be enabled for new
184- // device or when user has changed any of organization
185- // or backend field
186173 if ( isSelected === true ) {
187174 enabledTemplates . push ( templateId ) ;
188175 }
@@ -191,14 +178,20 @@ django.jQuery(function ($) {
191178 sortedm2mPrefixUl . append ( prefixElement ) ;
192179 }
193180 } ) ;
194- if ( firstRun === true && selectedTemplates !== undefined ) {
195- updateTemplateSelection ( selectedTemplates ) ;
196- }
197181 updateTemplateHelpText ( ) ;
198182 updateConfigTemplateField ( enabledTemplates ) ;
199183 } ) ;
200184 } ,
185+ initTemplateField = function ( ) {
186+ // sortedm2m generates a hidden input dynamically using rendered input checkbox elements,
187+ // but because the queryset is set to None in the Django admin, the input is created
188+ // without a name attribute. This workaround assigns the correct name to the hidden input.
189+ $ ( '.sortedm2m-container input[type="hidden"][id="undefined"]' )
190+ . first ( )
191+ . attr ( "name" , templatesFieldName ( ) ) ;
192+ } ,
201193 bindDefaultTemplateLoading = function ( ) {
194+ initTemplateField ( ) ;
202195 var backendField = $ ( backendFieldSelector ) ;
203196 $ ( orgFieldSelector ) . change ( function ( ) {
204197 // Only fetch templates when backend field is present
@@ -211,16 +204,18 @@ django.jQuery(function ($) {
211204 addChangeEventHandlerToBackendField ( ) ;
212205 } else if ( isDeviceGroup ( ) ) {
213206 // Initially request data to get templates
207+ initTemplateField ( ) ;
214208 showRelevantTemplates ( ) ;
215209 } else {
216210 // Add view: backendField is added when user adds configuration
217211 $ ( "#config-group > fieldset.module" ) . ready ( function ( ) {
218212 $ ( "div.add-row > a" ) . one ( "click" , function ( ) {
213+ initTemplateField ( ) ;
219214 addChangeEventHandlerToBackendField ( ) ;
220215 } ) ;
221216 } ) ;
222217 }
223- firstRun = false ;
218+ pageLoading = false ;
224219 $ ( "#content-main form" ) . submit ( function ( ) {
225220 $ (
226221 'ul.sortedm2m-items:first input[type="checkbox"][data-required="true"]' ,
0 commit comments