2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 from xml.dom import minidom
7 from grit.format.policy_templates.writers import xml_formatted_writer
10 def GetWriter(config):
11 '''Factory method for instanciating the ADMXWriter. Every Writer needs a
12 GetWriter method because the TemplateFormatter uses this method to
15 return ADMXWriter(['win'], config)
18 class ADMXWriter(xml_formatted_writer.XMLFormattedWriter):
19 '''Class for generating an ADMX policy template. It is used by the
20 PolicyTemplateGenerator to write the admx file.
23 # DOM root node of the generated ADMX document.
26 # The ADMX "policies" element that contains the ADMX "policy" elements that
28 _active_policies_elem = None
30 def _AdmlString(self, name):
31 '''Creates a reference to the named string in an ADML file.
33 name: Name of the referenced ADML string.
35 return '$(string.' + name + ')'
37 def _AdmlStringExplain(self, name):
38 '''Creates a reference to the named explanation string in an ADML file.
40 name: Name of the referenced ADML explanation.
42 return '$(string.' + name + '_Explain)'
44 def _AdmlPresentation(self, name):
45 '''Creates a reference to the named presentation element in an ADML file.
47 name: Name of the referenced ADML presentation element.
49 return '$(presentation.' + name + ')'
51 def _AddPolicyNamespaces(self, parent, prefix, namespace):
52 '''Generates the ADMX "policyNamespace" element and adds the elements to the
53 passed parent element. The namespace of the generated ADMX document is
54 define via the ADMX "target" element. Used namespaces are declared with an
55 ADMX "using" element. ADMX "target" and "using" elements are children of the
56 ADMX "policyNamespace" element.
59 parent: The parent node to which all generated elements are added.
60 prefix: A logical name that can be used in the generated ADMX document to
61 refere to this namespace.
62 namespace: Namespace of the generated ADMX document.
64 policy_namespaces_elem = self.AddElement(parent, 'policyNamespaces')
67 'namespace': namespace,
69 self.AddElement(policy_namespaces_elem, 'target', attributes)
72 'namespace': 'Microsoft.Policies.Windows',
74 self.AddElement(policy_namespaces_elem, 'using', attributes)
76 def _AddCategory(self, parent, name, display_name,
77 parent_category_name=None):
78 '''Adds an ADMX category element to the passed parent node. The following
79 snippet shows an example of a category element where "chromium" is the value
80 of the parameter name:
82 <category displayName="$(string.chromium)" name="chromium"/>
84 Each parent node can have only one category with a given name. Adding the
85 same category again with the same attributes is ignored, but adding it
86 again with different attributes is an error.
89 parent: The parent node to which all generated elements are added.
90 name: Name of the category.
91 display_name: Display name of the category.
92 parent_category_name: Name of the parent category. Defaults to None.
94 existing = filter(lambda e: e.getAttribute('name') == name,
95 parent.getElementsByTagName('category'))
97 assert len(existing) == 1
98 assert existing[0].getAttribute('name') == name
99 assert existing[0].getAttribute('displayName') == display_name
103 'displayName': display_name,
105 category_elem = self.AddElement(parent, 'category', attributes)
106 if parent_category_name:
107 attributes = {'ref': parent_category_name}
108 self.AddElement(category_elem, 'parentCategory', attributes)
110 def _AddCategories(self, categories):
111 '''Generates the ADMX "categories" element and adds it to the categories
112 main node. The "categories" element defines the category for the policies
113 defined in this ADMX document. Here is an example of an ADMX "categories"
117 <category displayName="$(string.google)" name="google"/>
118 <category displayName="$(string.googlechrome)" name="googlechrome">
119 <parentCategory ref="google"/>
124 categories_path: The categories path e.g. ['google', 'googlechrome']. For
125 each level in the path a "category" element will be generated. Except
126 for the root level, each level refers to its parent. Since the root
127 level category has no parent it does not require a parent reference.
130 for category in categories:
131 parent_category_name = category_name
132 category_name = category
133 self._AddCategory(self._categories_elem, category_name,
134 self._AdmlString(category_name), parent_category_name)
136 def _AddSupportedOn(self, parent, supported_os):
137 '''Generates the "supportedOn" ADMX element and adds it to the passed
138 parent node. The "supportedOn" element contains information about supported
139 Windows OS versions. The following code snippet contains an example of a
140 "supportedOn" element:
144 <definition name="SUPPORTED_WINXPSP2"
145 displayName="$(string.SUPPORTED_WINXPSP2)"/>
151 parent: The parent element to which all generated elements are added.
152 supported_os: List with all supported Win OSes.
154 supported_on_elem = self.AddElement(parent, 'supportedOn')
155 definitions_elem = self.AddElement(supported_on_elem, 'definitions')
157 'name': supported_os,
158 'displayName': self._AdmlString(supported_os)
160 self.AddElement(definitions_elem, 'definition', attributes)
162 def _AddStringPolicy(self, parent, name):
163 '''Generates ADMX elements for a String-Policy and adds them to the
170 self.AddElement(parent, 'text', attributes)
172 def _AddIntPolicy(self, parent, name):
173 '''Generates ADMX elements for an Int-Policy and adds them to the passed
179 'maxValue': '2000000000',
181 self.AddElement(parent, 'decimal', attributes)
183 def _AddEnumPolicy(self, parent, policy):
184 '''Generates ADMX elements for an Enum-Policy and adds them to the
185 passed parent element.
187 name = policy['name']
188 items = policy['items']
193 enum_elem = self.AddElement(parent, 'enum', attributes)
195 attributes = {'displayName': self._AdmlString(item['name'])}
196 item_elem = self.AddElement(enum_elem, 'item', attributes)
197 value_elem = self.AddElement(item_elem, 'value')
198 value_string = str(item['value'])
199 if policy['type'] == 'int-enum':
200 self.AddElement(value_elem, 'decimal', {'value': value_string})
202 self.AddElement(value_elem, 'string', {}, value_string)
204 def _AddListPolicy(self, parent, key, name):
205 '''Generates ADMX XML elements for a List-Policy and adds them to the
206 passed parent element.
209 # The ID must be in sync with ID of the corresponding element in the ADML
213 'key': key + '\\' + name,
215 self.AddElement(parent, 'list', attributes)
217 def _AddMainPolicy(self, parent):
218 '''Generates ADMX elements for a Main-Policy amd adds them to the
219 passed parent element.
221 enabled_value_elem = self.AddElement(parent, 'enabledValue');
222 self.AddElement(enabled_value_elem, 'decimal', {'value': '1'})
223 disabled_value_elem = self.AddElement(parent, 'disabledValue');
224 self.AddElement(disabled_value_elem, 'decimal', {'value': '0'})
226 def _GetElements(self, policy_group_elem):
227 '''Returns the ADMX "elements" child from an ADMX "policy" element. If the
228 "policy" element has no "elements" child yet, a new child is created.
231 policy_group_elem: The ADMX "policy" element from which the child element
232 "elements" is returned.
235 Exception: The policy_group_elem does not contain a ADMX "policy" element.
237 if policy_group_elem.tagName != 'policy':
238 raise Exception('Expected a "policy" element but got a "%s" element'
239 % policy_group_elem.tagName)
240 elements_list = policy_group_elem.getElementsByTagName('elements');
241 if len(elements_list) == 0:
242 return self.AddElement(policy_group_elem, 'elements')
243 elif len(elements_list) == 1:
244 return elements_list[0]
246 raise Exception('There is supposed to be only one "elements" node but'
247 ' there are %s.' % str(len(elements_list)))
249 def _WritePolicy(self, policy, name, key, parent):
250 '''Generates AMDX elements for a Policy. There are four different policy
251 types: Main-Policy, String-Policy, Enum-Policy and List-Policy.
253 policies_elem = self._active_policies_elem
254 policy_type = policy['type']
255 policy_name = policy['name']
256 if policy_type == 'external':
257 # This type can only be set through cloud policy.
262 'class': self.config['win_group_policy_class'],
263 'displayName': self._AdmlString(policy_name),
264 'explainText': self._AdmlStringExplain(policy_name),
265 'presentation': self._AdmlPresentation(policy_name),
268 # Store the current "policy" AMDX element in self for later use by the
269 # WritePolicy method.
270 policy_elem = self.AddElement(policies_elem, 'policy',
272 self.AddElement(policy_elem, 'parentCategory',
274 self.AddElement(policy_elem, 'supportedOn',
275 {'ref': self.config['win_supported_os']})
276 if policy_type == 'main':
277 self.AddAttribute(policy_elem, 'valueName', policy_name)
278 self._AddMainPolicy(policy_elem)
279 elif policy_type in ('string', 'dict'):
280 # 'dict' policies are configured as JSON-encoded strings on Windows.
281 parent = self._GetElements(policy_elem)
282 self._AddStringPolicy(parent, policy_name)
283 elif policy_type == 'int':
284 parent = self._GetElements(policy_elem)
285 self._AddIntPolicy(parent, policy_name)
286 elif policy_type in ('int-enum', 'string-enum'):
287 parent = self._GetElements(policy_elem)
288 self._AddEnumPolicy(parent, policy)
289 elif policy_type in ('list', 'string-enum-list'):
290 parent = self._GetElements(policy_elem)
291 self._AddListPolicy(parent, key, policy_name)
292 elif policy_type == 'group':
295 raise Exception('Unknown policy type %s.' % policy_type)
297 def WritePolicy(self, policy):
298 if self.CanBeMandatory(policy):
299 self._WritePolicy(policy,
301 self.config['win_reg_mandatory_key_name'],
302 self._active_mandatory_policy_group_name)
304 def WriteRecommendedPolicy(self, policy):
305 self._WritePolicy(policy,
306 policy['name'] + '_recommended',
307 self.config['win_reg_recommended_key_name'],
308 self._active_recommended_policy_group_name)
310 def _BeginPolicyGroup(self, group, name, parent):
311 '''Generates ADMX elements for a Policy-Group.
315 'displayName': self._AdmlString(group['name'] + '_group'),
317 category_elem = self.AddElement(self._categories_elem,
323 self.AddElement(category_elem, 'parentCategory', attributes)
325 def BeginPolicyGroup(self, group):
326 self._BeginPolicyGroup(group,
328 self.config['win_mandatory_category_path'][-1])
329 self._active_mandatory_policy_group_name = group['name']
331 def EndPolicyGroup(self):
332 self._active_mandatory_policy_group_name = \
333 self.config['win_mandatory_category_path'][-1]
335 def BeginRecommendedPolicyGroup(self, group):
336 self._BeginPolicyGroup(group,
337 group['name'] + '_recommended',
338 self.config['win_recommended_category_path'][-1])
339 self._active_recommended_policy_group_name = group['name'] + '_recommended'
341 def EndRecommendedPolicyGroup(self):
342 self._active_recommended_policy_group_name = \
343 self.config['win_recommended_category_path'][-1]
345 def BeginTemplate(self):
346 '''Generates the skeleton of the ADMX template. An ADMX template contains
347 an ADMX "PolicyDefinitions" element with four child nodes: "policies"
348 "policyNamspaces", "resources", "supportedOn" and "categories"
350 dom_impl = minidom.getDOMImplementation('')
351 self._doc = dom_impl.createDocument(None, 'policyDefinitions', None)
352 policy_definitions_elem = self._doc.documentElement
354 policy_definitions_elem.attributes['revision'] = '1.0'
355 policy_definitions_elem.attributes['schemaVersion'] = '1.0'
357 self._AddPolicyNamespaces(policy_definitions_elem,
358 self.config['admx_prefix'],
359 self.config['admx_namespace'])
360 self.AddElement(policy_definitions_elem, 'resources',
361 {'minRequiredRevision' : '1.0'})
362 self._AddSupportedOn(policy_definitions_elem,
363 self.config['win_supported_os'])
364 self._categories_elem = self.AddElement(policy_definitions_elem,
366 self._AddCategories(self.config['win_mandatory_category_path'])
367 self._AddCategories(self.config['win_recommended_category_path'])
368 self._active_policies_elem = self.AddElement(policy_definitions_elem,
370 self._active_mandatory_policy_group_name = \
371 self.config['win_mandatory_category_path'][-1]
372 self._active_recommended_policy_group_name = \
373 self.config['win_recommended_category_path'][-1]
375 def GetTemplateText(self):
376 return self.ToPrettyXml(self._doc)