1#!/usr/bin/env python
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.
5
6
7from xml.dom import minidom
8from grit.format.policy_templates.writers import plist_helper
9from grit.format.policy_templates.writers import xml_formatted_writer
10
11
12# This writer outputs a Preferences Manifest file as documented at
13# https://developer.apple.com/library/mac/documentation/MacOSXServer/Conceptual/Preference_Manifest_Files
14
15
16def GetWriter(config):
17  '''Factory method for creating PListWriter objects.
18  See the constructor of TemplateWriter for description of
19  arguments.
20  '''
21  return PListWriter(['mac'], config)
22
23
24class PListWriter(xml_formatted_writer.XMLFormattedWriter):
25  '''Class for generating policy templates in Mac plist format.
26  It is used by PolicyTemplateGenerator to write plist files.
27  '''
28
29  STRING_TABLE = 'Localizable.strings'
30  TYPE_TO_INPUT = {
31    'string': 'string',
32    'int': 'integer',
33    'int-enum': 'integer',
34    'string-enum': 'string',
35    'string-enum-list': 'array',
36    'main': 'boolean',
37    'list': 'array',
38    'dict': 'dictionary',
39  }
40
41  def _AddKeyValuePair(self, parent, key_string, value_tag):
42    '''Adds a plist key-value pair to a parent XML element.
43
44    A key-value pair in plist consists of two XML elements next two each other:
45    <key>key_string</key>
46    <value_tag>...</value_tag>
47
48    Args:
49      key_string: The content of the key tag.
50      value_tag: The name of the value element.
51
52    Returns:
53      The XML element of the value tag.
54    '''
55    self.AddElement(parent, 'key', {}, key_string)
56    return self.AddElement(parent, value_tag)
57
58  def _AddStringKeyValuePair(self, parent, key_string, value_string):
59    '''Adds a plist key-value pair to a parent XML element, where the
60    value element contains a string. The name of the value element will be
61    <string>.
62
63    Args:
64      key_string: The content of the key tag.
65      value_string: The content of the value tag.
66    '''
67    self.AddElement(parent, 'key', {}, key_string)
68    self.AddElement(parent, 'string', {}, value_string)
69
70  def _AddTargets(self, parent, policy):
71    '''Adds the following XML snippet to an XML element:
72      <key>pfm_targets</key>
73      <array>
74        <string>user-managed</string>
75      </array>
76
77      Args:
78        parent: The parent XML element where the snippet will be added.
79    '''
80    array = self._AddKeyValuePair(parent, 'pfm_targets', 'array')
81    if self.CanBeRecommended(policy):
82      self.AddElement(array, 'string', {}, 'user')
83    if self.CanBeMandatory(policy):
84      self.AddElement(array, 'string', {}, 'user-managed')
85
86  def PreprocessPolicies(self, policy_list):
87    return self.FlattenGroupsAndSortPolicies(policy_list)
88
89  def WritePolicy(self, policy):
90    policy_name = policy['name']
91    policy_type = policy['type']
92    if policy_type == 'external':
93      # This type can only be set through cloud policy.
94      return
95
96    dict = self.AddElement(self._array, 'dict')
97    self._AddStringKeyValuePair(dict, 'pfm_name', policy_name)
98    # Set empty strings for title and description. They will be taken by the
99    # OSX Workgroup Manager from the string table in a Localizable.strings file.
100    # Those files are generated by plist_strings_writer.
101    self._AddStringKeyValuePair(dict, 'pfm_description', '')
102    self._AddStringKeyValuePair(dict, 'pfm_title', '')
103    self._AddTargets(dict, policy)
104    self._AddStringKeyValuePair(dict, 'pfm_type',
105                                self.TYPE_TO_INPUT[policy_type])
106    if policy_type in ('int-enum', 'string-enum'):
107      range_list = self._AddKeyValuePair(dict, 'pfm_range_list', 'array')
108      for item in policy['items']:
109        if policy_type == 'int-enum':
110          element_type = 'integer'
111        else:
112          element_type = 'string'
113        self.AddElement(range_list, element_type, {}, str(item['value']))
114    elif policy_type in ('list', 'string-enum-list'):
115      subkeys = self._AddKeyValuePair(dict, 'pfm_subkeys', 'array')
116      subkeys_dict = self.AddElement(subkeys, 'dict')
117      subkeys_type = self._AddKeyValuePair(subkeys_dict, 'pfm_type', 'string')
118      self.AddText(subkeys_type, 'string')
119
120  def BeginTemplate(self):
121    self._plist.attributes['version'] = '1'
122    dict = self.AddElement(self._plist, 'dict')
123
124    app_name = plist_helper.GetPlistFriendlyName(self.config['app_name'])
125    self._AddStringKeyValuePair(dict, 'pfm_name', app_name)
126    self._AddStringKeyValuePair(dict, 'pfm_description', '')
127    self._AddStringKeyValuePair(dict, 'pfm_title', '')
128    self._AddStringKeyValuePair(dict, 'pfm_version', '1')
129    self._AddStringKeyValuePair(dict, 'pfm_domain',
130                                self.config['mac_bundle_id'])
131
132    self._array = self._AddKeyValuePair(dict, 'pfm_subkeys', 'array')
133
134  def CreatePlistDocument(self):
135    dom_impl = minidom.getDOMImplementation('')
136    doctype = dom_impl.createDocumentType(
137        'plist',
138        '-//Apple//DTD PLIST 1.0//EN',
139        'http://www.apple.com/DTDs/PropertyList-1.0.dtd')
140    return dom_impl.createDocument(None, 'plist', doctype)
141
142  def Init(self):
143    self._doc = self.CreatePlistDocument()
144    self._plist = self._doc.documentElement
145
146  def GetTemplateText(self):
147    return self.ToPrettyXml(self._doc)
148