1 #!/usr/bin/env python 2 3 """ 4 User profile management. 5 6 Copyright (C) 2015, 2016, 2017 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.config import settings 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 # Fake gettext method for strings to be translated later. 31 32 _ = lambda s: s 33 34 def identity_dict(l): 35 return dict([(i, i) for i in l]) 36 37 class Preferences(FileBase): 38 39 "A simple preferences file manager." 40 41 # See: docs/wiki/Preferences 42 43 known_keys = { 44 "CN" : "", 45 "TZID" : get_default_timezone(), 46 "LANG" : settings["LANG"], 47 "add_method_response" : settings["ADD_RESPONSE_DEFAULT"], 48 "event_refreshing" : settings["REFRESHING_DEFAULT"], 49 "freebusy_bundling" : settings["BUNDLING_DEFAULT"], 50 "freebusy_messages" : settings["NOTIFYING_DEFAULT"], 51 "freebusy_offers" : settings["FREEBUSY_OFFER_DEFAULT"], 52 "freebusy_publishing" : settings["PUBLISHING_DEFAULT"], 53 "freebusy_sharing" : settings["SHARING_DEFAULT"], 54 "incoming" : settings["INCOMING_DEFAULT"], 55 "organiser_replacement" : settings["ORGANISER_REPLACEMENT_DEFAULT"], 56 "participating" : settings["PARTICIPATING_DEFAULT"], 57 "permitted_times" : None, 58 } 59 60 known_key_choices = { 61 "TZID" : identity_dict(pytz.all_timezones), 62 "add_method_response" : { 63 "add" : _("Add events"), 64 "ignore" : _("Ignore requests"), 65 "refresh" : _("Ask for refreshed event details"), 66 }, 67 "event_refreshing" : { 68 "never" : _("Do not respond"), 69 "always" : _("Always respond"), 70 }, 71 "freebusy_bundling" : { 72 "never" : _("Never"), 73 "always" : _("Always"), 74 }, 75 "freebusy_messages" : { 76 "none" : _("Do not notify"), 77 "notify" : _("Notify"), 78 }, 79 "freebusy_publishing" : { 80 "publish" : _("Publish"), 81 "no" : _("Do not publish"), 82 }, 83 "freebusy_sharing" : { 84 "share" : _("Share"), 85 "no" : _("Do not share"), 86 }, 87 "incoming" : { 88 "message-only" : _("Original message only"), 89 "message-then-summary" : _("Original message followed by a separate summary message"), 90 "summary-then-message" : _("Summary message followed by the original message"), 91 "summary-only" : _("Summary message only"), 92 "summary-wraps-message" : _("Summary message wrapping the original message"), 93 }, 94 "organiser_replacement" : { 95 "any" : _("Anyone"), 96 "attendee" : _("Existing attendees only"), 97 "never" : _("Never allow organiser replacement"), 98 }, 99 "participating" : { 100 "participate" : _("Participate"), 101 "no" : _("Do not participate"), 102 } 103 } 104 105 def __init__(self, user, store_dir=None): 106 FileBase.__init__(self, store_dir or settings["PREFERENCES_DIR"]) 107 self.user = user 108 109 def get(self, name, default=None, config_default=False): 110 111 """ 112 Return the value for 'name', with absent entries providing a default of 113 None or any indicated 'default' or, if 'config_default' is set to a true 114 value, the default value from the config module. 115 """ 116 117 try: 118 return self[name] 119 except KeyError: 120 if config_default: 121 return self.known_keys.get(name, default) 122 else: 123 return default 124 125 def get_all(self, names): 126 127 """ 128 Return a dictionary containing values for entries having the given 129 'names'. Absent entries for names are omitted without error. 130 """ 131 132 d = {} 133 for name in names: 134 value = self.get(name) 135 if value is not None: 136 d[name] = value 137 return d 138 139 def has_key(self, name): 140 141 "Return whether an entry exists for 'name'." 142 143 try: 144 self[name] 145 return True 146 except KeyError: 147 return False 148 149 def keys(self): 150 151 "Return all entry names in the preferences." 152 153 filename = self.get_object_in_store(self.user) 154 if not filename or not isdir(filename): 155 return [] 156 157 return listdir(filename) 158 159 def items(self, all_known=False, default=None, config_default=False): 160 161 """ 162 Return all entries in the preferences or all known entries if 163 'all_known' is set to a true value, with absent entries providing a 164 default of None or any indicated 'default' or, if 'config_default' is 165 set to a true value, the default value from the config module. 166 167 Each entry will have the form (key, value). 168 """ 169 170 l = [] 171 for key in (all_known and self.known_keys or self).keys(): 172 l.append((key, self.get(key, default, config_default))) 173 return l 174 175 def choices(self, all_known=False, default=None, config_default=False): 176 177 """ 178 Return all entries in the preferences or all known entries if 179 'all_known' is set to a true value, with absent entries providing a 180 default of None or any indicated 'default' or, if 'config_default' is 181 set to a true value, the default value from the config module. 182 183 Each entry will have the form (key, value, choices). 184 """ 185 186 l = [] 187 for key, value in self.items(all_known, default, config_default): 188 l.append((key, value, self.known_key_choices.get(key))) 189 return l 190 191 def __getitem__(self, name): 192 193 "Return the value for 'name', raising a KeyError if absent." 194 195 filename = self.get_object_in_store(self.user, name) 196 if not filename or not exists(filename): 197 raise KeyError, name 198 199 f = codecs.open(filename, encoding="utf-8") 200 try: 201 return f.read().strip() 202 finally: 203 f.close() 204 205 def __setitem__(self, name, value): 206 207 "Set for 'name' the given 'value'." 208 209 filename = self.get_object_in_store(self.user, name) 210 if not filename: 211 return False 212 213 f = codecs.open(filename, "w", encoding="utf-8") 214 try: 215 f.write(value) 216 finally: 217 f.close() 218 fix_permissions(filename) 219 220 return True 221 222 # vim: tabstop=4 expandtab shiftwidth=4