1 #!/usr/bin/env python 2 3 import cgi, os, sys 4 5 sys.path.append("/var/lib/imip-agent") 6 7 from imiptools.content import format_datetime, get_datetime, get_item, \ 8 get_utc_datetime, get_values, parse_object, \ 9 to_timezone 10 from imiptools.period import have_conflict 11 import markup 12 import imip_store 13 14 getenv = os.environ.get 15 setenv = os.environ.__setitem__ 16 17 class CGIEnvironment: 18 19 "A CGI-compatible environment." 20 21 def __init__(self): 22 self.args = None 23 self.method = None 24 self.path = None 25 self.path_info = None 26 self.user = None 27 28 def get_args(self): 29 if self.args is None: 30 if self.get_method() != "POST": 31 setenv("QUERY_STRING", "") 32 self.args = cgi.parse(keep_blank_values=True) 33 return self.args 34 35 def get_method(self): 36 if self.method is None: 37 self.method = getenv("REQUEST_METHOD") or "GET" 38 return self.method 39 40 def get_path(self): 41 if self.path is None: 42 self.path = getenv("SCRIPT_NAME") or "" 43 return self.path 44 45 def get_path_info(self): 46 if self.path_info is None: 47 self.path_info = getenv("PATH_INFO") or "" 48 return self.path_info 49 50 def get_user(self): 51 if self.user is None: 52 self.user = getenv("REMOTE_USER") or "" 53 return self.user 54 55 def get_output(self): 56 return sys.stdout 57 58 def get_url(self): 59 path = self.get_path() 60 path_info = self.get_path_info() 61 return "%s%s" % (path.rstrip("/"), path_info) 62 63 class Manager: 64 65 "A simple manager application." 66 67 def __init__(self): 68 self.store = imip_store.FileStore() 69 self.env = CGIEnvironment() 70 user = self.env.get_user() 71 self.user = user and "mailto:%s" % user or None 72 self.out = self.env.get_output() 73 self.page = markup.page() 74 self.encoding = "utf-8" 75 76 def new_page(self, title): 77 self.page.init(title=title, charset=self.encoding) 78 79 def status(self, code, message): 80 print >>self.out, "Status:", code, message 81 82 def no_user(self): 83 self.status(403, "Forbidden") 84 self.new_page(title="Forbidden") 85 self.page.p("You are not logged in and thus cannot access scheduling requests.") 86 87 def no_page(self): 88 self.status(404, "Not Found") 89 self.new_page(title="Not Found") 90 self.page.p("No page is provided at the given address.") 91 92 def show_requests(self): 93 94 "Show requests for the current user." 95 96 # NOTE: This list could be more informative, but it is envisaged that 97 # NOTE: the requests would be visited directly anyway. 98 99 self.new_page(title="Pending Requests") 100 self.page.ul() 101 102 requests = self.store.get_requests(self.user) 103 104 for request in requests: 105 self.page.li() 106 self.page.a(request, href="%s/%s" % (self.env.get_url(), request)) 107 self.page.li.close() 108 109 self.page.ul.close() 110 111 def show_request(self, path_info): 112 113 "Show a request using the given 'path_info' for the current user." 114 115 uid = path_info.lstrip("/").split("/", 1)[0] 116 f = uid and self.store.get_event(self.user, uid) or None 117 118 if not f: 119 return False 120 121 request = parse_object(f, "utf-8") 122 123 if not request: 124 return False 125 126 # Handle a submitted form. 127 128 args = self.env.get_args() 129 show_form = False 130 131 if args.has_key("accept"): 132 pass 133 elif args.has_key("decline"): 134 pass 135 elif args.has_key("ignore"): 136 pass 137 else: 138 show_form = True 139 140 self.new_page(title="Request") 141 142 # Provide a summary of the request. 143 144 self.page.p("The following request was received:") 145 self.page.dl() 146 147 for name in ["SUMMARY", "DTSTART", "DTEND", "ORGANIZER", "ATTENDEE"]: 148 for value in get_values(request, name): 149 self.page.dt(name) 150 self.page.dd(value) 151 152 self.page.dl.close() 153 154 dtstart = format_datetime(get_utc_datetime(request, "DTSTART")) 155 dtend = format_datetime(get_utc_datetime(request, "DTEND")) 156 157 # Indicate whether there are conflicting events. 158 159 freebusy = self.store.get_freebusy(self.user) 160 if freebusy: 161 162 # Obtain any time zone details from the suggested event. 163 164 _dtstart, attr = get_item(request, "DTSTART") 165 tzid = attr.get("TZID") 166 167 # Show any conflicts. 168 169 for start, end, uid in have_conflict(freebusy, [(dtstart, dtend)], True): 170 start = format_datetime(to_timezone(get_datetime(start), tzid)) 171 end = format_datetime(to_timezone(get_datetime(end), tzid)) 172 self.page.p("Event conflicts with another from %s to %s." % (start, end)) 173 174 # Show a form if no action has just been taken. 175 176 if show_form: 177 self.page.p("Action to take for this request:") 178 self.page.form(method="POST") 179 self.page.p() 180 self.page.input(name="accept", type="submit", value="Accept") 181 self.page.add(" ") 182 self.page.input(name="decline", type="submit", value="Decline") 183 self.page.add(" ") 184 self.page.input(name="ignore", type="submit", value="Ignore") 185 self.page.p.close() 186 self.page.form.close() 187 188 return True 189 190 def select_action(self): 191 192 "Select the desired action and show the result." 193 194 path_info = self.env.get_path_info().rstrip("/") 195 if not path_info: 196 self.show_requests() 197 elif self.show_request(path_info): 198 pass 199 else: 200 self.no_page() 201 202 def show(self): 203 204 "Interpret a request and show an appropriate response." 205 206 if not self.user: 207 self.no_user() 208 else: 209 self.select_action() 210 211 # Write the headers and actual content. 212 213 print >>self.out, "Content-Type: text/html; charset=%s" % self.encoding 214 print >>self.out 215 self.out.write(unicode(self.page).encode(self.encoding)) 216 217 if __name__ == "__main__": 218 Manager().show() 219 220 # vim: tabstop=4 expandtab shiftwidth=4