paul@922 | 1 | #!/usr/bin/env python |
paul@922 | 2 | |
paul@922 | 3 | """ |
paul@922 | 4 | A Web interface to the user profile. |
paul@922 | 5 | |
paul@1120 | 6 | Copyright (C) 2015, 2016 Paul Boddie <paul@boddie.org.uk> |
paul@922 | 7 | |
paul@922 | 8 | This program is free software; you can redistribute it and/or modify it under |
paul@922 | 9 | the terms of the GNU General Public License as published by the Free Software |
paul@922 | 10 | Foundation; either version 3 of the License, or (at your option) any later |
paul@922 | 11 | version. |
paul@922 | 12 | |
paul@922 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@922 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@922 | 15 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@922 | 16 | details. |
paul@922 | 17 | |
paul@922 | 18 | You should have received a copy of the GNU General Public License along with |
paul@922 | 19 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@922 | 20 | """ |
paul@922 | 21 | |
paul@923 | 22 | from imipweb.resource import FormUtilities, ResourceClient |
paul@922 | 23 | |
paul@1008 | 24 | # Fake gettext method for strings to be translated later. |
paul@1008 | 25 | |
paul@1008 | 26 | _ = lambda s: s |
paul@1008 | 27 | |
paul@923 | 28 | class ProfilePage(ResourceClient, FormUtilities): |
paul@922 | 29 | |
paul@922 | 30 | "A request handler for the user profile page." |
paul@922 | 31 | |
paul@923 | 32 | # See: imiptools.config, imiptools.profile |
paul@999 | 33 | # NOTE: This could be defined in another format and generated or computed |
paul@999 | 34 | # NOTE: for this class and for the documentation. |
paul@999 | 35 | # See: docs/tools/profile_prefs.sh |
paul@922 | 36 | |
paul@923 | 37 | pref_labels = [ |
paul@1008 | 38 | ("participating" , _("Participate in the calendar system")), |
paul@1008 | 39 | ("CN" , _("Your common name")), |
paul@1008 | 40 | ("LANG" , _("Language")), |
paul@1008 | 41 | ("TZID" , _("Time zone/regime")), |
paul@1008 | 42 | ("incoming" , _("How to present incoming calendar messages")), |
paul@1008 | 43 | ("freebusy_sharing" , _("Share free/busy information")), |
paul@1008 | 44 | ("freebusy_bundling" , _("Bundle free/busy details with messages")), |
paul@1008 | 45 | ("freebusy_publishing" , _("Publish free/busy details via the Web")), |
paul@1008 | 46 | ("freebusy_messages" , _("Deliver details of received free/busy messages")), |
paul@1008 | 47 | ("add_method_response" , _("How to respond to messages adding events")), |
paul@1008 | 48 | ("event_refreshing" , _("How to handle event refresh requests")), |
paul@1008 | 49 | ("organiser_replacement" , _("Recognise whom as a new organiser of an event?")), |
paul@923 | 50 | ] |
paul@922 | 51 | |
paul@922 | 52 | def handle_request(self): |
paul@922 | 53 | args = self.env.get_args() |
paul@922 | 54 | save = args.has_key("save") |
paul@922 | 55 | cancel = args.has_key("cancel") |
paul@922 | 56 | action = save or cancel |
paul@922 | 57 | |
paul@922 | 58 | if not action: |
paul@922 | 59 | return ["action"] |
paul@922 | 60 | |
paul@1162 | 61 | # Check the validation token. |
paul@1162 | 62 | |
paul@1162 | 63 | if not self.check_validation_token(): |
paul@1162 | 64 | return ["token"] |
paul@1162 | 65 | |
paul@923 | 66 | if save: |
paul@923 | 67 | errors = self.update_preferences() |
paul@923 | 68 | if errors: |
paul@923 | 69 | return errors |
paul@923 | 70 | else: |
paul@923 | 71 | self.redirect(self.link_to()) |
paul@923 | 72 | |
paul@923 | 73 | elif cancel: |
paul@923 | 74 | self.redirect(self.link_to()) |
paul@923 | 75 | |
paul@922 | 76 | return None |
paul@922 | 77 | |
paul@923 | 78 | def update_preferences(self): |
paul@923 | 79 | |
paul@923 | 80 | "Update the stored preferences." |
paul@923 | 81 | |
paul@923 | 82 | settings = self.get_current_preferences() |
paul@923 | 83 | prefs = self.get_preferences() |
paul@923 | 84 | errors = [] |
paul@923 | 85 | |
paul@923 | 86 | for name, value in settings.items(): |
paul@923 | 87 | choices = prefs.known_key_choices.get(name) |
paul@923 | 88 | if choices and not choices.has_key(value): |
paul@923 | 89 | errors.append(name) |
paul@923 | 90 | |
paul@923 | 91 | if errors: |
paul@923 | 92 | return errors |
paul@923 | 93 | |
paul@923 | 94 | for name, value in settings.items(): |
paul@923 | 95 | prefs[name] = value |
paul@923 | 96 | |
paul@923 | 97 | # Request logic methods. |
paul@923 | 98 | |
paul@923 | 99 | def is_initial_load(self): |
paul@923 | 100 | |
paul@923 | 101 | "Return whether the event is being loaded and shown for the first time." |
paul@923 | 102 | |
paul@923 | 103 | return not self.env.get_args().has_key("editing") |
paul@923 | 104 | |
paul@923 | 105 | def get_stored_preferences(self): |
paul@923 | 106 | |
paul@923 | 107 | "Return stored preference information for the current user." |
paul@923 | 108 | |
paul@923 | 109 | prefs = self.get_preferences() |
paul@923 | 110 | return dict(prefs.items()) |
paul@923 | 111 | |
paul@923 | 112 | def get_current_preferences(self): |
paul@923 | 113 | |
paul@923 | 114 | "Return the preferences currently being edited." |
paul@923 | 115 | |
paul@923 | 116 | if self.is_initial_load(): |
paul@923 | 117 | return self.get_stored_preferences() |
paul@923 | 118 | else: |
paul@923 | 119 | return dict([(name, values and values[0] or "") for (name, values) in self.env.get_args().items()]) |
paul@923 | 120 | |
paul@922 | 121 | # Output fragment methods. |
paul@922 | 122 | |
paul@922 | 123 | def show_preferences(self, errors=None): |
paul@923 | 124 | |
paul@923 | 125 | "Show the preferences, indicating any 'errors' in the output." |
paul@923 | 126 | |
paul@1005 | 127 | _ = self.get_translator() |
paul@1005 | 128 | |
paul@922 | 129 | page = self.page |
paul@923 | 130 | settings = self.get_current_preferences() |
paul@923 | 131 | prefs = self.get_preferences() |
paul@923 | 132 | |
paul@923 | 133 | # Add a hidden control to help determine whether editing has already begun. |
paul@923 | 134 | |
paul@923 | 135 | self.control("editing", "hidden", "true") |
paul@922 | 136 | |
paul@922 | 137 | # Show the range of preferences, getting all possible entries and using |
paul@922 | 138 | # configuration defaults. |
paul@922 | 139 | |
paul@922 | 140 | page.table(class_="profile", cellspacing=5, cellpadding=5) |
paul@922 | 141 | page.thead() |
paul@922 | 142 | page.tr() |
paul@1120 | 143 | page.th(_("Preferences"), class_="mainheading", colspan=2) |
paul@922 | 144 | page.tr.close() |
paul@922 | 145 | page.thead.close() |
paul@922 | 146 | page.tbody() |
paul@922 | 147 | |
paul@923 | 148 | for name, label in self.pref_labels: |
paul@923 | 149 | value = settings.get(name) |
paul@923 | 150 | default = prefs.known_keys.get(name) |
paul@923 | 151 | choices = prefs.known_key_choices.get(name) |
paul@922 | 152 | |
paul@922 | 153 | page.tr() |
paul@922 | 154 | page.th(class_="profileheading %s%s" % (name, errors and name in errors and " error" or "")) |
paul@1008 | 155 | page.label(_(label), for_=name) |
paul@922 | 156 | page.th.close() |
paul@922 | 157 | page.td() |
paul@923 | 158 | |
paul@1005 | 159 | # For unrestricted fields, show a text field. |
paul@1005 | 160 | |
paul@923 | 161 | if not choices: |
paul@923 | 162 | page.input(name=name, value=(value or default), type="text", class_="preference", id_=name) |
paul@1005 | 163 | |
paul@1005 | 164 | # Otherwise, obtain the choices, localise the labels and show a |
paul@1005 | 165 | # menu control. |
paul@1005 | 166 | |
paul@923 | 167 | else: |
paul@1008 | 168 | choices = [(key, _(value_label)) for (key, value_label) in choices.items()] |
paul@923 | 169 | choices.sort() |
paul@977 | 170 | self.menu(name, default, choices, value is not None and [value] or None, class_="preference") |
paul@923 | 171 | |
paul@922 | 172 | page.td.close() |
paul@922 | 173 | page.tr.close() |
paul@922 | 174 | |
paul@922 | 175 | page.tbody.close() |
paul@922 | 176 | page.table.close() |
paul@922 | 177 | |
paul@923 | 178 | def show_controls(self): |
paul@923 | 179 | |
paul@923 | 180 | "Show controls for performing actions." |
paul@923 | 181 | |
paul@1008 | 182 | _ = self.get_translator() |
paul@1008 | 183 | |
paul@923 | 184 | page = self.page |
paul@923 | 185 | |
paul@923 | 186 | page.p(class_="controls") |
paul@1008 | 187 | page.input(name="save", type="submit", value=_("Save")) |
paul@1008 | 188 | page.input(name="cancel", type="submit", value=_("Cancel")) |
paul@923 | 189 | page.p.close() |
paul@923 | 190 | |
paul@922 | 191 | # Full page output methods. |
paul@922 | 192 | |
paul@922 | 193 | def show(self): |
paul@922 | 194 | |
paul@922 | 195 | "Show the preferences of a user." |
paul@922 | 196 | |
paul@923 | 197 | page = self.page |
paul@922 | 198 | errors = self.handle_request() |
paul@922 | 199 | |
paul@922 | 200 | if not errors: |
paul@922 | 201 | return True |
paul@922 | 202 | |
paul@1008 | 203 | _ = self.get_translator() |
paul@1008 | 204 | |
paul@1008 | 205 | self.new_page(title=_("Profile")) |
paul@923 | 206 | page.form(method="POST") |
paul@1162 | 207 | self.validator() |
paul@922 | 208 | self.show_preferences(errors) |
paul@923 | 209 | self.show_controls() |
paul@923 | 210 | page.form.close() |
paul@922 | 211 | |
paul@922 | 212 | return True |
paul@922 | 213 | |
paul@922 | 214 | # vim: tabstop=4 expandtab shiftwidth=4 |