1
1
"use strict" ;
2
2
django . jQuery ( function ( $ ) {
3
- var firstRun = true ,
3
+ var pageLoading = true ,
4
4
backendFieldSelector = "#id_config-0-backend" ,
5
5
orgFieldSelector = "#id_organization" ,
6
6
isDeviceGroup = function ( ) {
7
- return window . _deviceGroup ;
7
+ return window . _deviceGroupId !== undefined ;
8
8
} ,
9
9
templatesFieldName = function ( ) {
10
10
return isDeviceGroup ( ) ? "templates" : "config-0-templates" ;
11
11
} ,
12
+ isAddingNewObject = function ( ) {
13
+ return isDeviceGroup ( )
14
+ ? ! $ ( ".add-form" ) . length
15
+ : $ ( 'input[name="config-0-id"]' ) . val ( ) . length === 0 ;
16
+ } ,
12
17
getTemplateOptionElement = function (
13
18
index ,
14
19
templateId ,
@@ -33,23 +38,15 @@ django.jQuery(function ($) {
33
38
if ( templateConfig . required ) {
34
39
inputField . prop ( "disabled" , true ) ;
35
40
}
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 ) {
37
43
inputField . prop ( "checked" , true ) ;
38
44
}
39
45
return element ;
40
46
} ,
41
47
resetTemplateOptions = function ( ) {
42
48
$ ( "ul.sortedm2m-items" ) . empty ( ) ;
43
49
} ,
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
- } ,
53
50
updateTemplateHelpText = function ( ) {
54
51
var helpText = "Choose items and order by drag & drop." ;
55
52
if ( $ ( "li.sortedm2m-item:first" ) . length === 0 ) {
@@ -73,11 +70,32 @@ django.jQuery(function ($) {
73
70
showRelevantTemplates ( ) ;
74
71
} ,
75
72
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 ;
81
99
} ,
82
100
parseSelectedTemplates = function ( selectedTemplates ) {
83
101
if ( selectedTemplates !== undefined ) {
@@ -88,85 +106,57 @@ django.jQuery(function ($) {
88
106
}
89
107
}
90
108
} ,
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
+ } ,
91
125
showRelevantTemplates = function ( ) {
92
126
var orgID = $ ( orgFieldSelector ) . val ( ) ,
93
127
backend = isDeviceGroup ( ) ? "" : $ ( backendFieldSelector ) . val ( ) ,
94
- selectedTemplates ;
128
+ currentSelection = getSelectedTemplates ( ) ;
95
129
96
130
// 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 ) ) {
98
132
resetTemplateOptions ( ) ;
99
133
updateTemplateHelpText ( ) ;
100
134
return ;
101
135
}
102
136
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 ) ;
118
138
$ . get ( url ) . done ( function ( data ) {
119
139
resetTemplateOptions ( ) ;
120
140
var enabledTemplates = [ ] ,
121
141
sortedm2mUl = $ ( "ul.sortedm2m-items:first" ) ,
122
142
sortedm2mPrefixUl = $ ( "ul.sortedm2m-items:last" ) ;
123
143
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
160
145
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 ;
166
146
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 ) ) ,
170
160
element = getTemplateOptionElement (
171
161
index ,
172
162
templateId ,
@@ -180,9 +170,6 @@ django.jQuery(function ($) {
180
170
isSelected ,
181
171
true ,
182
172
) ;
183
- // Default templates should only be enabled for new
184
- // device or when user has changed any of organization
185
- // or backend field
186
173
if ( isSelected === true ) {
187
174
enabledTemplates . push ( templateId ) ;
188
175
}
@@ -191,14 +178,20 @@ django.jQuery(function ($) {
191
178
sortedm2mPrefixUl . append ( prefixElement ) ;
192
179
}
193
180
} ) ;
194
- if ( firstRun === true && selectedTemplates !== undefined ) {
195
- updateTemplateSelection ( selectedTemplates ) ;
196
- }
197
181
updateTemplateHelpText ( ) ;
198
182
updateConfigTemplateField ( enabledTemplates ) ;
199
183
} ) ;
200
184
} ,
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
+ } ,
201
193
bindDefaultTemplateLoading = function ( ) {
194
+ initTemplateField ( ) ;
202
195
var backendField = $ ( backendFieldSelector ) ;
203
196
$ ( orgFieldSelector ) . change ( function ( ) {
204
197
// Only fetch templates when backend field is present
@@ -211,16 +204,18 @@ django.jQuery(function ($) {
211
204
addChangeEventHandlerToBackendField ( ) ;
212
205
} else if ( isDeviceGroup ( ) ) {
213
206
// Initially request data to get templates
207
+ initTemplateField ( ) ;
214
208
showRelevantTemplates ( ) ;
215
209
} else {
216
210
// Add view: backendField is added when user adds configuration
217
211
$ ( "#config-group > fieldset.module" ) . ready ( function ( ) {
218
212
$ ( "div.add-row > a" ) . one ( "click" , function ( ) {
213
+ initTemplateField ( ) ;
219
214
addChangeEventHandlerToBackendField ( ) ;
220
215
} ) ;
221
216
} ) ;
222
217
}
223
- firstRun = false ;
218
+ pageLoading = false ;
224
219
$ ( "#content-main form" ) . submit ( function ( ) {
225
220
$ (
226
221
'ul.sortedm2m-items:first input[type="checkbox"][data-required="true"]' ,
0 commit comments