1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - EventAggregatorNewEvent Action 4 5 @copyright: 2008, 2009, 2010, 2011 by Paul Boddie <paul@boddie.org.uk> 6 @copyright: 2000-2004 Juergen Hermann <jh@web.de>, 7 2003-2008 MoinMoin:ThomasWaldmann, 8 2004-2006 MoinMoin:AlexanderSchremmer, 9 2007 MoinMoin:ReimarBauer. 10 @license: GNU GPL (v2 or later), see COPYING.txt for details. 11 """ 12 13 from MoinMoin.action import ActionBase 14 from MoinMoin.Page import Page 15 from MoinMoin.PageEditor import PageEditor 16 from MoinMoin import config 17 from EventAggregatorSupport import * 18 19 try: 20 import pytz 21 except ImportError: 22 pytz = None 23 24 Dependencies = ['pages'] 25 26 # Action class and supporting functions. 27 28 class EventAggregatorNewEvent(ActionBase, ActionSupport): 29 30 "An event creation dialogue requesting various parameters." 31 32 def get_form_html(self, buttons_html): 33 _ = self._ 34 request = self.request 35 form = self.get_form() 36 37 # Handle advanced and basic forms, and enable/disable certain fields. 38 39 show_advanced = form.get("advanced") and not form.get("basic") 40 show_end_date = form.get("end-day") and not form.get("hide-end-date") or form.get("show-end-date") 41 show_times = (form.get("start-hour") or form.get("end-hour")) and not form.get("hide-times") or form.get("show-times") 42 43 show_zone_regime = form.get("regime") and not form.get("show-offsets") and \ 44 not form.get("hide-zone") or form.get("show-regime") 45 show_zone_offsets = form.get("start-offset") and not form.get("show-regime") and \ 46 not form.get("hide-zone") or form.get("show-offsets") 47 48 show_zones = show_zone_regime or show_zone_offsets 49 50 # Prepare the category list. 51 52 category_list = [] 53 category_pagenames = form.get("category", []) 54 55 for category_name, category_pagename in getCategoryMapping(getCategories(request), request): 56 57 selected = self._get_selected_for_list(category_pagename, category_pagenames) 58 59 # In the advanced view, populate a menu. 60 61 if show_advanced: 62 category_list.append('<option value="%s" %s>%s</option>' % ( 63 escattr(category_pagename), selected, escape(category_name))) 64 65 # In the basic view, use hidden fields. 66 67 elif selected: 68 category_list.append('<input value="%s" name="category" type="hidden" />' % escattr(category_pagename)) 69 70 # Prepare the topics list. 71 72 topics = form.get("topics", []) 73 74 if form.get("add-topic"): 75 topics.append("") 76 else: 77 for i in range(0, len(topics)): 78 if form.get("remove-topic-%d" % i): 79 del topics[i] 80 break 81 82 # Initialise month lists. 83 84 start_month_list, end_month_list = self.get_month_lists(default_as_current=1) 85 start_year_default, end_year_default = self.get_year_defaults(default_as_current=1) 86 87 # Initialise regime lists. 88 89 regime_list = [] 90 regime_list.append('<option value=""></option>') 91 92 regime = form.get("regime", [None])[0] 93 94 # Prepare regime lists, selecting specified regimes. 95 96 if pytz is not None: 97 for pytz_regime in pytz.common_timezones: 98 selected = self._get_selected(pytz_regime, regime) 99 regime_list.append('<option value="%s" %s>%s</option>' % (escattr(pytz_regime), selected, escape(pytz_regime))) 100 101 # Permitting configuration of the template name. 102 103 template_default = getattr(request.cfg, "event_aggregator_new_event_template", "EventTemplate") 104 105 d = { 106 "buttons_html" : buttons_html, 107 "form_trigger" : escattr(self.form_trigger), 108 "form_cancel" : escattr(self.form_cancel), 109 110 "category_label" : escape(_("Categories")), 111 "category_list" : "\n".join(category_list), 112 113 "start_month_list" : "\n".join(start_month_list), 114 "end_month_list" : "\n".join(end_month_list), 115 116 "regime_list" : "\n".join(regime_list), 117 "use_regime_label" : escape(_("Using local time")), 118 119 "show_end_date_label" : escape(_("Specify end date")), 120 "hide_end_date_label" : escape(_("End event on same day")), 121 122 "show_times_label" : escape(_("Specify times")), 123 "hide_times_label" : escape(_("No start and end times")), 124 125 "show_offsets_label" : escape(_("Specify UTC offsets")), 126 "show_regime_label" : escape(_("Specify location")), 127 "hide_zone_label" : escape(_("Make times apply everywhere")), 128 129 "start_label" : escape(_("Start date (day, month, year)")), 130 "start_day_default" : escattr(form.get("start-day", [""])[0]), 131 "start_year_default" : escattr(start_year_default), 132 "start_time_label" : escape(_("Start time (hour, minute, second)")), 133 "start_hour_default" : escattr(form.get("start-hour", [""])[0]), 134 "start_minute_default" : escattr(form.get("start-minute", [""])[0]), 135 "start_second_default" : escattr(form.get("start-second", [""])[0]), 136 "start_offset_default" : escattr(form.get("start-offset", [""])[0]), 137 138 "end_label" : escape(_("End date (day, month, year) - if different")), 139 "end_day_default" : escattr(form.get("end-day", [""])[0] or form.get("start-day", [""])[0]), 140 "end_year_default" : escattr(end_year_default), 141 "end_time_label" : escape(_("End time (hour, minute, second)")), 142 "end_hour_default" : escattr(form.get("end-hour", [""])[0]), 143 "end_minute_default" : escattr(form.get("end-minute", [""])[0]), 144 "end_second_default" : escattr(form.get("end-second", [""])[0]), 145 "end_offset_default" : escattr(form.get("end-offset", [""])[0] or form.get("start-offset", [""])[0]), 146 147 "title_label" : escape(_("Event title/summary")), 148 "title_default" : escattr(form.get("title", [""])[0]), 149 "description_label" : escape(_("Event description")), 150 "description_default" : escattr(form.get("description", [""])[0]), 151 "location_label" : escape(_("Event location")), 152 "location_default" : escattr(form.get("location", [""])[0]), 153 "latitude_label" : escape(_("Latitude")), 154 "latitude_default" : escattr(form.get("latitude", [""])[0]), 155 "longitude_label" : escape(_("Longitude")), 156 "longitude_default" : escattr(form.get("longitude", [""])[0]), 157 "link_label" : escape(_("Event URL")), 158 "link_default" : escattr(form.get("link", [""])[0]), 159 160 "topics_label" : escape(_("Topics")), 161 "add_topic_label" : escape(_("Add topic")), 162 "remove_topic_label" : escape(_("Remove topic")), 163 164 "template_label" : escape(_("Event template")), 165 "template_default" : escattr(form.get("template", [""])[0] or template_default), 166 "parent_label" : escape(_("Parent page")), 167 "parent_default" : escattr(form.get("parent", [""])[0]), 168 169 "advanced_label" : escape(_("Show advanced options")), 170 "basic_label" : escape(_("Hide advanced options")), 171 } 172 173 # Prepare the output HTML. 174 175 html = ''' 176 <input name="update-form-only" value="false" type="hidden" /> 177 <table> 178 <tr> 179 <td class="label"><label>%(title_label)s</label></td> 180 <td colspan="2"> 181 <input name="title" type="text" size="40" value="%(title_default)s" /> 182 </td> 183 </tr> 184 <tr> 185 <td class="label"><label>%(start_label)s</label></td> 186 <td colspan="2" style="white-space: nowrap"> 187 <input name="start-day" type="text" value="%(start_day_default)s" size="2" /> 188 <select name="start-month"> 189 %(start_month_list)s 190 </select> 191 <input name="start-year" type="text" value="%(start_year_default)s" size="4" /> 192 </td> 193 </tr>''' % d 194 195 # End date controls. 196 197 if show_end_date: 198 html += ''' 199 <tr> 200 <td class="label"><label>%(end_label)s</label></td> 201 <td colspan="2" style="white-space: nowrap"> 202 <input name="end-day" type="text" value="%(end_day_default)s" size="2" /> 203 <select name="end-month"> 204 %(end_month_list)s 205 </select> 206 <input name="end-year" type="text" value="%(end_year_default)s" size="4" /> 207 </td> 208 </tr> 209 <tr> 210 <td class="label"> 211 <input name="hide-end-date" type="submit" value="%(hide_end_date_label)s" /> 212 </td> 213 </tr>''' % d 214 else: 215 html += ''' 216 <tr> 217 <td class="label"> 218 <input name="show-end-date" type="submit" value="%(show_end_date_label)s" /> 219 </td> 220 </tr>''' % d 221 222 # Generic time information. 223 224 if show_times: 225 226 # Start time controls. 227 228 html += ''' 229 <tr> 230 <td class="label"><label>%(start_time_label)s</label></td> 231 <td style="white-space: nowrap"> 232 <input name="start-hour" type="text" value="%(start_hour_default)s" size="2" maxlength="2" /> 233 <input name="start-minute" type="text" value="%(start_minute_default)s" size="2" maxlength="2" /> 234 <input name="start-second" type="text" value="%(start_second_default)s" size="2" maxlength="2" /> 235 </td>''' % d 236 237 # Offset information displayed. 238 239 if show_zone_offsets: 240 html += ''' 241 <td> 242 UTC <input name="start-offset" type="text" value="%(start_offset_default)s" size="6" maxlength="6" /> 243 </td>''' % d 244 245 # Regime information displayed. 246 247 elif show_zone_regime: 248 html += ''' 249 <td> 250 %(use_regime_label)s 251 </td>''' % d 252 253 html += ''' 254 </tr>''' 255 256 # End time controls. 257 258 html += ''' 259 <tr> 260 <td class="label"><label>%(end_time_label)s</label></td> 261 <td style="white-space: nowrap"> 262 <input name="end-hour" type="text" value="%(end_hour_default)s" size="2" maxlength="2" /> 263 <input name="end-minute" type="text" value="%(end_minute_default)s" size="2" maxlength="2" /> 264 <input name="end-second" type="text" value="%(end_second_default)s" size="2" maxlength="2" /> 265 </td>''' % d 266 267 # Offset information displayed. 268 269 if show_zone_offsets: 270 html += ''' 271 <td> 272 UTC <input name="end-offset" type="text" value="%(end_offset_default)s" size="6" maxlength="6" /> 273 </td>''' % d 274 275 # Regime information displayed. 276 277 elif show_zone_regime: 278 html += ''' 279 <td> 280 <select name="regime"> 281 %(regime_list)s 282 </select> 283 </td>''' % d 284 285 # Controls for removing times. 286 287 html += ''' 288 </tr> 289 <tr> 290 <td class="label"> 291 <input name="hide-times" type="submit" value="%(hide_times_label)s" /> 292 </td> 293 <td></td>''' % d 294 295 # Time zone controls. 296 297 if show_zones: 298 299 # Offset information displayed. 300 301 if show_zone_offsets: 302 html += ''' 303 <td> 304 <input name="show-regime" type="submit" value="%(show_regime_label)s" />''' % d 305 306 # Regime information displayed. 307 308 elif show_zone_regime: 309 html += ''' 310 <td> 311 <input name="show-offsets" type="submit" value="%(show_offsets_label)s" />''' % d 312 313 # To remove zone information. 314 315 html += ''' 316 <input name="hide-zone" type="submit" value="%(hide_zone_label)s" /> 317 </td>''' % d 318 319 # No time zone information shown. 320 321 else: 322 html += ''' 323 <td> 324 <input name="show-regime" type="submit" value="%(show_regime_label)s" /> 325 <input name="show-offsets" type="submit" value="%(show_offsets_label)s" /> 326 </td>''' % d 327 328 html += ''' 329 </tr>''' 330 331 # Controls for adding times. 332 333 else: 334 html += ''' 335 <tr> 336 <td class="label"> 337 <input name="show-times" type="submit" value="%(show_times_label)s" /> 338 </td> 339 </tr>''' % d 340 341 342 # Various basic controls. 343 344 html += ''' 345 <tr> 346 <td class="label"><label>%(description_label)s</label></td> 347 <td colspan="2"> 348 <input name="description" type="text" size="40" value="%(description_default)s" /> 349 </td> 350 </tr> 351 <tr> 352 <td class="label"><label>%(location_label)s</label></td> 353 <td colspan="2"> 354 <input name="location" type="text" size="40" value="%(location_default)s" /> 355 </td> 356 </tr> 357 <tr> 358 <td class="label"><label>%(latitude_label)s</label></td> 359 <td colspan="2"> 360 <input name="latitude" type="text" size="40" value="%(latitude_default)s" /> 361 </td> 362 </tr> 363 <tr> 364 <td class="label"><label>%(longitude_label)s</label></td> 365 <td colspan="2"> 366 <input name="longitude" type="text" size="40" value="%(longitude_default)s" /> 367 </td> 368 </tr> 369 <tr> 370 <td class="label"><label>%(link_label)s</label></td> 371 <td colspan="2"> 372 <input name="link" type="text" size="40" value="%(link_default)s" /> 373 </td> 374 </tr>''' % d 375 376 # Topics. 377 378 for i, topic in enumerate(topics): 379 d["topic"] = escattr(topic) 380 d["topic_number"] = i 381 html += ''' 382 <tr> 383 <td class="label"><label>%(topics_label)s</label></td> 384 <td colspan="2"> 385 <input name="topics" type="text" size="20" value="%(topic)s" /> 386 <input name="remove-topic-%(topic_number)s" type="submit" value="%(remove_topic_label)s" /> 387 </td> 388 </tr>''' % d 389 390 html += ''' 391 <tr> 392 <td></td> 393 <td colspan="2"> 394 <input name="add-topic" type="submit" value="%(add_topic_label)s" /> 395 </td> 396 </tr>''' % d 397 398 # Advanced options. 399 400 if show_advanced: 401 html += ''' 402 <tr> 403 <td></td> 404 <td colspan="2"> 405 <input name="basic" type="submit" value="%(basic_label)s" /> 406 <input name="advanced" type="hidden" value="true" /> 407 </td> 408 </tr> 409 <tr> 410 <td class="label"><label>%(category_label)s</label></td> 411 <td colspan="2" class="content"> 412 <select multiple="multiple" name="category"> 413 %(category_list)s 414 </select> 415 </td> 416 </tr> 417 <tr> 418 <td class="label"><label>%(template_label)s</label></td> 419 <td colspan="2"> 420 <input name="template" type="text" size="40" value="%(template_default)s" /> 421 </td> 422 </tr> 423 <tr> 424 <td class="label"><label>%(parent_label)s</label></td> 425 <td colspan="2"> 426 <input name="parent" type="text" size="40" value="%(parent_default)s" /> 427 </td> 428 </tr> 429 <tr> 430 <td></td> 431 <td colspan="2" class="buttons"> 432 %(buttons_html)s 433 </td> 434 </tr> 435 </table>''' % d 436 else: 437 html += ''' 438 <tr> 439 <td></td> 440 <td colspan="2"> 441 <input name="advanced" type="submit" value="%(advanced_label)s" /> 442 %(category_list)s 443 <input name="parent" type="hidden" value="%(parent_default)s" /> 444 <input name="template" type="hidden" value="%(template_default)s" /> 445 </td> 446 </tr> 447 <tr> 448 <td></td> 449 <td colspan="2" class="buttons"> 450 %(buttons_html)s 451 </td> 452 </tr> 453 </table> 454 <script type="text/javascript"> 455 function replaceDialog(url, button) { 456 var form = findForm(); 457 var dialog = findDialog(document); 458 if (form != null && dialog != null) { 459 var xmlhttp = new XMLHttpRequest(); 460 xmlhttp.open("POST", url, false); 461 xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 462 463 var requestBody = encodeURIComponent(button.name) + "=" + encodeURIComponent(button.value); 464 for (var i = 0; i < form.elements.length; i++) { 465 var element = form.elements[i]; 466 if (element.type != "submit") { 467 requestBody += "&" + encodeURIComponent(element.name) + "=" + encodeURIComponent(element.value); 468 } 469 } 470 xmlhttp.send(requestBody); 471 472 var newDialog = xmlhttp.responseText; 473 474 if (newDialog != null) { 475 dialog.parentNode.innerHTML = newDialog; 476 initForm(); 477 return false; 478 } 479 } 480 } 481 482 function findDialog(d) { 483 var elements = d.getElementsByTagName("div"); 484 for (var i = 0; i < elements.length; i++) { 485 var element = elements[i]; 486 var cls = element.getAttribute("class"); 487 if (cls == "dialog") { 488 return element; 489 } 490 } 491 return null; 492 } 493 494 function findForm() { 495 for (var i = 0; i < document.forms.length; i++) { 496 var form = document.forms[i]; 497 if (form["update-form-only"] != null) { 498 return form; 499 } 500 } 501 return null; 502 } 503 504 function initForm() { 505 var form = findForm(); 506 var url = form.getAttribute("action"); 507 form["update-form-only"].value = "true"; 508 for (var i = 0; i < form.length; i++) { 509 var element = form[i]; 510 if (element.type == "submit" && element.name != "%(form_trigger)s" && element.name != "%(form_cancel)s") { 511 element.setAttribute("onclick", "return replaceDialog('" + url + "', this);"); 512 } 513 } 514 } 515 516 initForm(); 517 </script>''' % d 518 519 return html 520 521 def do_action(self): 522 523 "Create the new event." 524 525 _ = self._ 526 form = self.get_form() 527 528 # If no title exists in the request, an error message is returned. 529 530 title = form.get("title", [None])[0] 531 template = form.get("template", [None])[0] 532 533 if not title: 534 return 0, _("No event title specified.") 535 536 if not template: 537 return 0, _("No page template specified.") 538 539 return self.create_event(self.request) 540 541 def render_msg(self, msg, msgtype): 542 543 """ 544 Render 'msg' and 'msgtype'. If 'msgtype' is "dialog" then the form is 545 rendered, and if only part of the form is being requested, the output 546 will be only the form HTML fragment and not the entire page. 547 """ 548 549 # Either render the form as a fragment of a page. 550 551 print >>open("/tmp/out.txt", "a"), self.get_form().get("update-form-only") 552 if msgtype == "dialog" and self.get_form().get("update-form-only", ["false"])[0] == "true": 553 send_headers = get_send_headers(self.request) 554 send_headers(["Content-Type: text/html; charset=%s" % config.charset]) 555 self.request.write(msg.render()) 556 557 # Or render the message/form within an entire page. 558 559 else: 560 ActionBase.render_msg(self, msg, msgtype) 561 562 def render_success(self, msg, msgtype): 563 564 """ 565 Render neither 'msg' nor 'msgtype' since redirection should occur 566 instead. 567 """ 568 569 pass 570 571 def create_event(self, request): 572 573 "Create an event page using the 'request'." 574 575 _ = request.getText 576 form = self.get_form() 577 578 category_pagenames = form.get("category", []) 579 description = form.get("description", [None])[0] 580 location = form.get("location", [None])[0] 581 latitude = form.get("latitude", [None])[0] 582 longitude = form.get("longitude", [None])[0] 583 link = form.get("link", [None])[0] 584 topics = form.get("topics", []) 585 586 regime = form.get("regime", [None])[0] 587 start_offset = form.get("start-offset", [None])[0] 588 end_offset = form.get("end-offset", [None])[0] 589 590 start_zone = regime or start_offset 591 end_zone = regime or end_offset 592 593 # Validate certain fields. 594 595 try: 596 title = form["title"][0] 597 template = form["template"][0] 598 parent = form["parent"][0] 599 except (KeyError, IndexError): 600 return 0, _("Event title or template missing.") 601 602 try: 603 start_day = self._get_input(form, "start-day") 604 start_month = self._get_input(form, "start-month") 605 start_year = self._get_input(form, "start-year") 606 607 if not start_day or not start_month or not start_year: 608 return 0, _("A start date must be specified.") 609 610 end_day = self._get_input(form, "end-day", start_day) 611 end_month = self._get_input(form, "end-month", start_month) 612 end_year = self._get_input(form, "end-year", start_year) 613 614 except (TypeError, ValueError): 615 return 0, _("Days and years must be numbers yielding a valid date!") 616 617 try: 618 start_hour = self._get_input(form, "start-hour") 619 start_minute = self._get_input(form, "start-minute") 620 start_second = self._get_input(form, "start-second") 621 622 end_hour = self._get_input(form, "end-hour") 623 end_minute = self._get_input(form, "end-minute") 624 end_second = self._get_input(form, "end-second") 625 626 except (TypeError, ValueError): 627 return 0, _("Hours, minutes and seconds must be numbers yielding a valid time!") 628 629 start_date = DateTime( 630 (start_year, start_month, start_day, start_hour, start_minute, start_second, start_zone) 631 ) 632 start_date.constrain() 633 634 end_date = DateTime( 635 (end_year, end_month, end_day, end_hour, end_minute, end_second, end_zone) 636 ) 637 end_date.constrain() 638 639 # An elementary date ordering check. 640 641 if (start_date.as_date() != end_date.as_date() or start_date.has_time() and end_date.has_time()) and start_date > end_date: 642 start_date, end_date = end_date, start_date 643 644 event_details = { 645 "start" : str(start_date), "end" : str(end_date), 646 "title" : title, "summary" : title, 647 "description" : description, "location" : location, "link" : link, 648 "topics" : [topic for topic in topics if topic] 649 } 650 651 if latitude and longitude: 652 event_details["geo"] = latitude, longitude 653 654 # Copy the template. 655 656 template_page = PageEditor(request, template) 657 658 if not template_page.exists(): 659 return 0, _("Event template not available.") 660 661 # Use any parent page information. 662 663 full_title = getFullPageName(parent, title) 664 665 # Load the new page and replace the event details in the body. 666 667 new_page = PageEditor(request, full_title) 668 669 if new_page.exists(): 670 return 0, _("The specified page already exists. Please choose another name.") 671 672 # Complete the new page. 673 674 fillEventPageFromTemplate(template_page, new_page, event_details, category_pagenames) 675 676 # Redirect and return success. 677 678 query = {'action' : 'edit'} 679 request.http_redirect(new_page.url(request, query)) 680 return 1, None 681 682 # Action function. 683 684 def execute(pagename, request): 685 EventAggregatorNewEvent(pagename, request).render() 686 687 # vim: tabstop=4 expandtab shiftwidth=4