1 #!/usr/bin/env python 2 3 """ 4 User profile management. 5 6 Copyright (C) 2015 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from imiptools import config 23 from imiptools.dates import get_default_timezone 24 from imiptools.filesys import fix_permissions, FileBase 25 from os.path import exists, isdir 26 from os import listdir, makedirs 27 import codecs 28 import pytz 29 30 def identity_dict(l): 31 return dict([(i, i) for i in l]) 32 33 class Preferences(FileBase): 34 35 "A simple preferences file manager." 36 37 # See: docs/preferences.txt 38 39 known_keys = { 40 "CN" : "", 41 "LANG" : config.LANG, 42 "TZID" : get_default_timezone(), 43 "add_method_response" : config.ADD_RESPONSE_DEFAULT, 44 "event_refreshing" : config.REFRESHING_DEFAULT, 45 "freebusy_bundling" : config.BUNDLING_DEFAULT, 46 "freebusy_messages" : config.NOTIFYING_DEFAULT, 47 "freebusy_offers" : config.FREEBUSY_OFFER_DEFAULT, 48 "freebusy_publishing" : config.PUBLISHING_DEFAULT, 49 "freebusy_sharing" : config.SHARING_DEFAULT, 50 "incoming" : config.INCOMING_DEFAULT, 51 "organiser_replacement" : config.ORGANISER_REPLACEMENT_DEFAULT, 52 "participating" : config.PARTICIPATING_DEFAULT, 53 "permitted_times" : None, 54 } 55 56 known_key_choices = { 57 "TZID" : identity_dict(pytz.all_timezones), 58 "add_method_response" : { 59 "add" : "Add events", 60 "ignore" : "Ignore requests", 61 "refresh" : "Ask for refreshed event details" 62 }, 63 "event_refreshing" : { 64 "never" : "Do not respond", 65 "always" : "Always respond" 66 }, 67 "freebusy_bundling" : { 68 "never" : "Never", 69 "always" : "Always" 70 }, 71 "freebusy_messages" : { 72 "none" : "Do not notify", 73 "notify" : "Notify" 74 }, 75 "freebusy_publishing" : { 76 "publish" : "Publish", 77 "no" : "Do not publish" 78 }, 79 "freebusy_sharing" : { 80 "share" : "Share", 81 "no" : "Do not share" 82 }, 83 "incoming" : { 84 "message-only" : "Original message only", 85 "message-then-summary" : "Original message followed by a separate summary message", 86 "summary-then-message" : "Summary message followed by the original message", 87 "summary-only" : "Summary message only", 88 "summary-wraps-message" : "Summary message wrapping the original message" 89 }, 90 "organiser_replacement" : { 91 "any" : "Anyone", 92 "attendee" : "Existing attendees only", 93 "never" : "Never allow organiser replacement" 94 }, 95 "participating" : { 96 "participate" : "Participate", 97 "no" : "Do not participate" 98 } 99 } 100 101 def __init__(self, user, store_dir=None): 102 FileBase.__init__(self, store_dir or config.PREFERENCES_DIR) 103 self.user = user 104 105 def get(self, name, default=None, config_default=False): 106 107 """ 108 Return the value for 'name', with absent entries providing a default of 109 None or any indicated 'default' or, if 'config_default' is set to a true 110 value, the default value from the config module. 111 """ 112 113 try: 114 return self[name] 115 except KeyError: 116 if config_default: 117 return self.known_keys.get(name, default) 118 else: 119 return default 120 121 def get_all(self, names): 122 123 """ 124 Return a dictionary containing values for entries having the given 125 'names'. Absent entries for names are omitted without error. 126 """ 127 128 d = {} 129 for name in names: 130 value = self.get(name) 131 if value is not None: 132 d[name] = value 133 return d 134 135 def has_key(self, name): 136 137 "Return whether an entry exists for 'name'." 138 139 try: 140 self[name] 141 return True 142 except KeyError: 143 return False 144 145 def keys(self): 146 147 "Return all entry names in the preferences." 148 149 filename = self.get_object_in_store(self.user) 150 if not filename or not isdir(filename): 151 return [] 152 153 return listdir(filename) 154 155 def items(self, all_known=False, default=None, config_default=False): 156 157 """ 158 Return all entries in the preferences or all known entries if 159 'all_known' is set to a true value, with absent entries providing a 160 default of None or any indicated 'default' or, if 'config_default' is 161 set to a true value, the default value from the config module. 162 163 Each entry will have the form (key, value). 164 """ 165 166 l = [] 167 for key in (all_known and self.known_keys or self).keys(): 168 l.append((key, self.get(key, default, config_default))) 169 return l 170 171 def choices(self, all_known=False, default=None, config_default=False): 172 173 """ 174 Return all entries in the preferences or all known entries if 175 'all_known' is set to a true value, with absent entries providing a 176 default of None or any indicated 'default' or, if 'config_default' is 177 set to a true value, the default value from the config module. 178 179 Each entry will have the form (key, value, choices). 180 """ 181 182 l = [] 183 for key, value in self.items(all_known, default, config_default): 184 l.append((key, value, self.known_key_choices.get(key))) 185 return l 186 187 def __getitem__(self, name): 188 189 "Return the value for 'name', raising a KeyError if absent." 190 191 filename = self.get_object_in_store(self.user, name) 192 if not filename or not exists(filename): 193 raise KeyError, name 194 195 f = codecs.open(filename, encoding="utf-8") 196 try: 197 return f.read().strip() 198 finally: 199 f.close() 200 201 def __setitem__(self, name, value): 202 203 "Set for 'name' the given 'value'." 204 205 filename = self.get_object_in_store(self.user, name) 206 if not filename: 207 return False 208 209 f = codecs.open(filename, "w", encoding="utf-8") 210 try: 211 f.write(value) 212 finally: 213 f.close() 214 fix_permissions(filename) 215 216 return True 217 218 # vim: tabstop=4 expandtab shiftwidth=4