1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - EventAggregatorNewEvent Action 4 5 @copyright: 2008, 2009, 2010, 2011, 2012 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 import re 19 20 try: 21 import pytz 22 except ImportError: 23 pytz = None 24 25 Dependencies = ['pages'] 26 27 # Action class and supporting functions. 28 29 class EventAggregatorNewEvent(ActionBase, ActionSupport): 30 31 "An event creation dialogue requesting various parameters." 32 33 def get_form_html(self, buttons_html): 34 _ = self._ 35 request = self.request 36 form = self.get_form() 37 38 # Handle advanced and basic forms, and enable/disable certain fields. 39 40 show_advanced = form.get("advanced") and not form.get("basic") 41 show_end_date = form.get("end-day") and not form.get("hide-end-date") or form.get("show-end-date") 42 show_times = (form.get("start-hour") or form.get("end-hour")) and not form.get("hide-times") or form.get("show-times") 43 show_location = form.get("show-location") or form.get("new-location") and not form.get("hide-location") 44 choose_page_name = form.get("page-name") and not form.get("auto-page-name") or form.get("choose-page-name") 45 46 # Define the overridden page name, if appropriate. 47 48 page_name = choose_page_name and form.get("page-name", ["@PARENT@/@TITLE@"])[0] or "" 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 the locations list. 88 89 locations_page = getLocationsPage(request) 90 locations = getWikiDict(locations_page, request) 91 92 locations_list = [] 93 locations_list.append('<option value=""></option>') 94 95 location = (form.get("location") or form.get("new-location") or [""])[0] 96 97 # Prepare the locations list, selecting the specified location. 98 99 if locations: 100 locations_list += self.get_option_list(location, locations) or [] 101 102 locations_list.sort() 103 104 # Initialise the time regime from the location. 105 106 start_regime = form.get("start-regime", [None])[0] 107 end_regime = form.get("end-regime", [None])[0] 108 109 if not start_regime: 110 start_regime = Location(location, locations).getTimeRegime() 111 end_regime = end_regime or start_regime 112 113 # Initialise regime lists. 114 115 start_regime_list = [] 116 start_regime_list.append('<option value="">%s</option>' % escape(_("<The event location (if known)>"))) 117 end_regime_list = [] 118 end_regime_list.append('<option value="">%s</option>' % escape(_("<Same as start time>"))) 119 120 # Prepare regime lists, selecting specified regimes. 121 122 if pytz is not None: 123 start_regime_list += self.get_option_list(start_regime, pytz.common_timezones) 124 end_regime_list += self.get_option_list(end_regime, pytz.common_timezones) 125 126 # Show time zone-related information depending on various fields. 127 128 show_zone_regime = ( 129 form.get("start-regime") # have a regime 130 and not form.get("show-offsets") # are not switching to offsets 131 and not form.get("hide-zone") # are not removing zone information 132 or form.get("show-regime") # are switching to a regime 133 or form.get("show-times") and start_regime # are showing times with a regime 134 ) 135 show_zone_offsets = ( 136 form.get("start-offset") # have an offset 137 and not form.get("show-regime") # are not switching to a regime 138 and not form.get("hide-zone") # are not removing zone information 139 or form.get("show-offsets") # are switching to offsets 140 ) 141 142 show_zones = show_zone_regime or show_zone_offsets 143 144 # Permitting configuration of the template name. 145 146 template_default = getattr(request.cfg, "event_aggregator_new_event_template", "EventTemplate") 147 148 # Help page location. 149 150 help_page_name = getattr(request.cfg, "event_aggregator_new_event_help", "HelpOnEventAggregatorNewEvent") 151 help_page_url = Page(request, help_page_name).url(request) 152 153 # Substitution of parameters. 154 155 d = { 156 "buttons_html" : buttons_html, 157 "form_trigger" : escattr(self.form_trigger), 158 "form_cancel" : escattr(self.form_cancel), 159 160 "category_label" : escape(_("Categories")), 161 "category_list" : "\n".join(category_list), 162 163 "start_month_list" : "\n".join(start_month_list), 164 "end_month_list" : "\n".join(end_month_list), 165 166 "start_regime_list" : "\n".join(start_regime_list), 167 "end_regime_list" : "\n".join(end_regime_list), 168 "use_regime_label" : escape(_("Using local time in...")), 169 170 "show_end_date_label" : escape(_("Specify end date")), 171 "hide_end_date_label" : escape(_("End event on same day")), 172 173 "show_times_label" : escape(_("Specify times")), 174 "hide_times_label" : escape(_("No start and end times")), 175 176 "show_offsets_label" : escape(_("Specify UTC offsets")), 177 "show_regime_label" : escape(_("Use local time")), 178 "hide_zone_label" : escape(_("Make times apply everywhere")), 179 180 "start_label" : escape(_("Start date (day, month, year)")), 181 "start_day_default" : escattr(form.get("start-day", [""])[0]), 182 "start_year_default" : escattr(start_year_default), 183 "start_time_label" : escape(_("Start time (hour, minute, second)")), 184 "start_hour_default" : escattr(form.get("start-hour", [""])[0]), 185 "start_minute_default" : escattr(form.get("start-minute", [""])[0]), 186 "start_second_default" : escattr(form.get("start-second", [""])[0]), 187 "start_offset_default" : escattr(form.get("start-offset", [""])[0]), 188 189 "end_label" : escape(_("End date (day, month, year) - if different")), 190 "end_day_default" : escattr(form.get("end-day", [""])[0].strip() or form.get("start-day", [""])[0]), 191 "end_year_default" : escattr(end_year_default), 192 "end_time_label" : escape(_("End time (hour, minute, second)")), 193 "end_hour_default" : escattr(form.get("end-hour", [""])[0]), 194 "end_minute_default" : escattr(form.get("end-minute", [""])[0]), 195 "end_second_default" : escattr(form.get("end-second", [""])[0]), 196 "end_offset_default" : escattr(form.get("end-offset", [""])[0].strip() or form.get("start-offset", [""])[0]), 197 198 "title_label" : escape(_("Event title/summary")), 199 "title_default" : escattr(form.get("title", [""])[0]), 200 "choose_page_name_label": escape(_("Choose page name")), 201 "auto_page_name_label" : escape(_("Auto page name")), 202 "page_name_label" : escape(_("Page name")), 203 "page_name_default" : escattr(form.get("page-name", [page_name])[0]), 204 "description_label" : escape(_("Event description")), 205 "description_default" : escattr(form.get("description", [""])[0]), 206 207 "location_label" : escape(_("Event location")), 208 "locations_list" : "\n".join(locations_list), 209 "show_location_label" : escattr(_("Specify a different location")), 210 "hide_location_label" : escattr(_("Choose a known location")), 211 "new_location" : escattr(form.get("new-location", [location])[0]), 212 213 "latitude_label" : escape(_("Latitude")), 214 "latitude_default" : escattr(form.get("latitude", [""])[0]), 215 "longitude_label" : escape(_("Longitude")), 216 "longitude_default" : escattr(form.get("longitude", [""])[0]), 217 "link_label" : escape(_("External URL")), 218 "link_default" : escattr(form.get("link", [""])[0]), 219 220 "topics_label" : escape(_("Topics")), 221 "add_topic_label" : escape(_("Add topic")), 222 "remove_topic_label" : escape(_("Remove topic")), 223 224 "template_label" : escape(_("Event template")), 225 "template_default" : escattr(form.get("template", [""])[0].strip() or template_default), 226 "parent_label" : escape(_("Parent page")), 227 "parent_default" : escattr(form.get("parent", [""])[0]), 228 229 "advanced_label" : escape(_("Show advanced options")), 230 "basic_label" : escape(_("Hide advanced options")), 231 232 "help_page_url" : escattr(help_page_url), 233 "help_page_label" : escape(_("Help on creating events")), 234 } 235 236 # Prepare the output HTML. 237 238 html = ''' 239 <input name="update-form-only" value="false" type="hidden" /> 240 <table> 241 <tr> 242 <td class="label"><label>%(title_label)s</label></td> 243 <td colspan="2"> 244 <input name="title" type="text" size="40" value="%(title_default)s" /> 245 </td> 246 </tr>''' % d 247 248 # Page name options. 249 250 if choose_page_name: 251 html += ''' 252 <tr> 253 <td class="label"><label>%(page_name_label)s</label></td> 254 <td colspan="2"> 255 <input name="page-name" type="text" size="40" value="%(page_name_default)s" /> 256 </td> 257 <tr> 258 <td class="label"> 259 <input name="auto-page-name" type="submit" value="%(auto_page_name_label)s" /> 260 </td> 261 </tr>''' % d 262 else: 263 html += ''' 264 <tr> 265 <td class="label"> 266 <input name="choose-page-name" type="submit" value="%(choose_page_name_label)s" /> 267 </td> 268 </tr>''' % d 269 270 # Location options. 271 272 html += ''' 273 <tr> 274 <td class="label"><label>%(location_label)s</label></td> 275 <td colspan="2">''' % d 276 277 if not show_location: 278 html += ''' 279 <select name="location"> 280 %(locations_list)s 281 </select> 282 <input name="show-location" type="submit" value="%(show_location_label)s" />''' % d 283 284 else: 285 html += ''' 286 <input name="new-location" type="text" size="30" value="%(new_location)s" /> 287 <input name="hide-location" type="submit" value="%(hide_location_label)s" />''' % d 288 289 html += ''' 290 </td> 291 </tr>''' % d 292 293 # Latitude and longitude. 294 295 if show_location: 296 html += ''' 297 <tr> 298 <td class="label"><label>%(latitude_label)s</label></td> 299 <td colspan="2"> 300 <input name="latitude" type="text" size="40" value="%(latitude_default)s" /> 301 </td> 302 </tr> 303 <tr> 304 <td class="label"><label>%(longitude_label)s</label></td> 305 <td colspan="2"> 306 <input name="longitude" type="text" size="40" value="%(longitude_default)s" /> 307 </td> 308 </tr>''' % d 309 310 # Start date controls. 311 312 html += ''' 313 <tr> 314 <td class="label"><label>%(start_label)s</label></td> 315 <td colspan="2" style="white-space: nowrap"> 316 <input name="start-day" type="text" value="%(start_day_default)s" size="2" /> 317 <select name="start-month"> 318 %(start_month_list)s 319 </select> 320 <input name="start-year" type="text" value="%(start_year_default)s" size="4" /> 321 </td> 322 </tr>''' % d 323 324 # End date controls. 325 326 if show_end_date: 327 html += ''' 328 <tr> 329 <td class="label"><label>%(end_label)s</label></td> 330 <td colspan="2" style="white-space: nowrap"> 331 <input name="end-day" type="text" value="%(end_day_default)s" size="2" /> 332 <select name="end-month"> 333 %(end_month_list)s 334 </select> 335 <input name="end-year" type="text" value="%(end_year_default)s" size="4" /> 336 </td> 337 </tr> 338 <tr> 339 <td class="label"> 340 <input name="hide-end-date" type="submit" value="%(hide_end_date_label)s" /> 341 </td> 342 </tr>''' % d 343 else: 344 html += ''' 345 <tr> 346 <td class="label"> 347 <input name="show-end-date" type="submit" value="%(show_end_date_label)s" /> 348 </td> 349 </tr>''' % d 350 351 # Generic time information. 352 353 if show_times: 354 355 # Start time controls. 356 357 html += ''' 358 <tr> 359 <td class="label event-time-selection"><label>%(start_time_label)s</label></td> 360 <td style="white-space: nowrap" class="event-time-selection"> 361 <input name="start-hour" type="text" value="%(start_hour_default)s" size="2" maxlength="2" /> 362 <input name="start-minute" type="text" value="%(start_minute_default)s" size="2" maxlength="2" /> 363 <input name="start-second" type="text" value="%(start_second_default)s" size="2" maxlength="2" /> 364 </td>''' % d 365 366 # Offset information displayed. 367 368 if show_zone_offsets: 369 html += ''' 370 <td class="event-zone-selection"> 371 UTC <input name="start-offset" type="text" value="%(start_offset_default)s" size="6" maxlength="6" /> 372 </td>''' % d 373 374 # Regime information displayed. 375 376 elif show_zone_regime: 377 html += ''' 378 <td class="event-regime-selection"> 379 <label>%(use_regime_label)s</label><br/> 380 <select name="start-regime"> 381 %(start_regime_list)s 382 </select> 383 </td>''' % d 384 385 html += ''' 386 </tr>''' 387 388 # End time controls. 389 390 html += ''' 391 <tr> 392 <td class="label event-time-selection"><label>%(end_time_label)s</label></td> 393 <td style="white-space: nowrap" class="event-time-selection"> 394 <input name="end-hour" type="text" value="%(end_hour_default)s" size="2" maxlength="2" /> 395 <input name="end-minute" type="text" value="%(end_minute_default)s" size="2" maxlength="2" /> 396 <input name="end-second" type="text" value="%(end_second_default)s" size="2" maxlength="2" /> 397 </td>''' % d 398 399 # Offset information displayed. 400 401 if show_zone_offsets: 402 html += ''' 403 <td class="event-zone-selection"> 404 UTC <input name="end-offset" type="text" value="%(end_offset_default)s" size="6" maxlength="6" /> 405 </td>''' % d 406 407 # Regime information displayed. 408 409 elif show_zone_regime: 410 html += ''' 411 <td class="event-regime-selection event-end-time"> 412 <select name="end-regime"> 413 %(end_regime_list)s 414 </select> 415 </td>''' % d 416 417 # Controls for removing times. 418 419 html += ''' 420 </tr> 421 <tr> 422 <td class="label"> 423 <input name="hide-times" type="submit" value="%(hide_times_label)s" /> 424 </td> 425 <td></td> 426 <td>''' % d 427 428 # Time zone controls. 429 430 if show_zones: 431 432 # To remove zone information. 433 434 html += ''' 435 <input name="hide-zone" type="submit" value="%(hide_zone_label)s" />''' % d 436 437 # No time zone information shown. 438 439 else: 440 html += ''' 441 <input name="show-regime" type="submit" value="%(show_regime_label)s" /> 442 <input name="show-offsets" type="submit" value="%(show_offsets_label)s" />''' % d 443 444 html += ''' 445 </td> 446 </tr>''' 447 448 # Controls for adding times. 449 450 else: 451 html += ''' 452 <tr> 453 <td class="label"> 454 <input name="show-times" type="submit" value="%(show_times_label)s" /> 455 </td> 456 </tr>''' % d 457 458 459 # Various basic controls. 460 461 html += ''' 462 <tr> 463 <td class="label"><label>%(description_label)s</label></td> 464 <td colspan="2"> 465 <input name="description" type="text" size="40" value="%(description_default)s" /> 466 </td> 467 </tr> 468 <tr> 469 <td class="label"><label>%(link_label)s</label></td> 470 <td colspan="2"> 471 <input name="link" type="text" size="40" value="%(link_default)s" /> 472 </td> 473 </tr>''' % d 474 475 # Topics. 476 477 for i, topic in enumerate(topics): 478 d["topic"] = escattr(topic) 479 d["topic_number"] = i 480 html += ''' 481 <tr> 482 <td class="label"><label>%(topics_label)s</label></td> 483 <td colspan="2"> 484 <input name="topics" type="text" size="20" value="%(topic)s" /> 485 <input name="remove-topic-%(topic_number)s" type="submit" value="%(remove_topic_label)s" /> 486 </td> 487 </tr>''' % d 488 489 html += ''' 490 <tr> 491 <td></td> 492 <td colspan="2"> 493 <input name="add-topic" type="submit" value="%(add_topic_label)s" /> 494 </td> 495 </tr>''' % d 496 497 # Advanced options. 498 499 if show_advanced: 500 html += ''' 501 <tr> 502 <td></td> 503 <td colspan="2"> 504 <input name="basic" type="submit" value="%(basic_label)s" /> 505 <input name="advanced" type="hidden" value="true" /> 506 </td> 507 </tr> 508 <tr> 509 <td class="label"><label>%(category_label)s</label></td> 510 <td colspan="2" class="content"> 511 <select multiple="multiple" name="category"> 512 %(category_list)s 513 </select> 514 </td> 515 </tr> 516 <tr> 517 <td class="label"><label>%(template_label)s</label></td> 518 <td colspan="2"> 519 <input name="template" type="text" size="40" value="%(template_default)s" /> 520 </td> 521 </tr> 522 <tr> 523 <td class="label"><label>%(parent_label)s</label></td> 524 <td colspan="2"> 525 <input name="parent" type="text" size="40" value="%(parent_default)s" /> 526 </td> 527 </tr> 528 <tr> 529 <td></td> 530 <td colspan="2" class="buttons"> 531 %(buttons_html)s 532 </td> 533 </tr> 534 </table>''' % d 535 else: 536 html += ''' 537 <tr> 538 <td></td> 539 <td colspan="2"> 540 <input name="advanced" type="submit" value="%(advanced_label)s" /> 541 %(category_list)s 542 <input name="parent" type="hidden" value="%(parent_default)s" /> 543 <input name="template" type="hidden" value="%(template_default)s" /> 544 </td> 545 </tr> 546 <tr> 547 <td></td> 548 <td colspan="2" class="buttons"> 549 %(buttons_html)s 550 </td> 551 </tr> 552 <tr> 553 <td></td> 554 <td colspan="2"> 555 <a href="%(help_page_url)s" target="_blank">%(help_page_label)s</a> 556 </td> 557 </tr> 558 </table> 559 <script type="text/javascript"> 560 function replaceDialog(url, button) { 561 var form = findForm(); 562 var dialog = findDialog(document); 563 if (form != null && dialog != null) { 564 var xmlhttp = new XMLHttpRequest(); 565 xmlhttp.open("POST", url, false); 566 xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 567 568 var requestBody = encodeURIComponent(button.name) + "=" + encodeURIComponent(button.value); 569 for (var i = 0; i < form.elements.length; i++) { 570 var element = form.elements[i]; 571 if (element.type != "submit") { 572 requestBody += "&" + encodeURIComponent(element.name) + "=" + encodeURIComponent(element.value); 573 } 574 } 575 xmlhttp.send(requestBody); 576 577 var newDialog = xmlhttp.responseText; 578 579 if (newDialog != null) { 580 dialog.parentNode.innerHTML = newDialog; 581 initForm(); 582 return false; 583 } 584 } 585 } 586 587 function findDialog(d) { 588 var elements = d.getElementsByTagName("div"); 589 for (var i = 0; i < elements.length; i++) { 590 var element = elements[i]; 591 var cls = element.getAttribute("class"); 592 if (cls == "dialog") { 593 return element; 594 } 595 } 596 return null; 597 } 598 599 function findForm() { 600 for (var i = 0; i < document.forms.length; i++) { 601 var form = document.forms[i]; 602 if (form["update-form-only"] != null) { 603 return form; 604 } 605 } 606 return null; 607 } 608 609 function initForm() { 610 var form = findForm(); 611 var url = form.getAttribute("action"); 612 form["update-form-only"].value = "true"; 613 for (var i = 0; i < form.length; i++) { 614 var element = form[i]; 615 if (element.type == "submit" && element.name != "%(form_trigger)s" && element.name != "%(form_cancel)s") { 616 element.setAttribute("onclick", "return replaceDialog('" + url + "', this);"); 617 } 618 } 619 } 620 621 initForm(); 622 </script>''' % d 623 624 return html 625 626 def do_action(self): 627 628 "Create the new event." 629 630 _ = self._ 631 form = self.get_form() 632 633 # If no title exists in the request, an error message is returned. 634 635 title = form.get("title", [None])[0] 636 template = form.get("template", [None])[0] 637 638 if not title: 639 return 0, _("No event title specified.") 640 641 if not template: 642 return 0, _("No page template specified.") 643 644 return self.create_event(self.request) 645 646 def render_msg(self, msg, msgtype): 647 648 """ 649 Render 'msg' and 'msgtype'. If 'msgtype' is "dialog" then the form is 650 rendered, and if only part of the form is being requested, the output 651 will be only the form HTML fragment and not the entire page. 652 """ 653 654 # Either render the form as a fragment of a page. 655 656 form = self.get_form() 657 update_form_only = form.get("update-form-only", ["false"])[0] == "true" 658 action_attempted = form.has_key(self.form_trigger) 659 660 if msgtype == "dialog" and update_form_only and not action_attempted: 661 send_headers = get_send_headers(self.request) 662 send_headers(["Content-Type: text/html; charset=%s" % config.charset]) 663 self.request.write(msg.render()) 664 665 # Or render the message/form within an entire page. 666 667 else: 668 ActionBase.render_msg(self, msg, msgtype) 669 670 def render_success(self, msg, msgtype): 671 672 """ 673 Render neither 'msg' nor 'msgtype' since redirection should occur 674 instead. 675 """ 676 677 pass 678 679 def create_event(self, request): 680 681 "Create an event page using the 'request'." 682 683 _ = request.getText 684 form = self.get_form() 685 686 category_pagenames = form.get("category", []) 687 description = form.get("description", [None])[0] 688 location = (form.get("location") or form.get("new-location") or [""])[0] 689 latitude = form.get("latitude", [None])[0] 690 longitude = form.get("longitude", [None])[0] 691 link = form.get("link", [None])[0] 692 topics = form.get("topics", []) 693 694 start_regime = form.get("start-regime", [None])[0] 695 end_regime = form.get("end-regime", form.get("start-regime", [None]))[0] 696 start_offset = form.get("start-offset", [None])[0] 697 end_offset = form.get("end-offset", [None])[0] 698 699 start_zone = start_regime or start_offset 700 end_zone = end_regime or end_offset 701 702 page_name = form.get("page-name", [None])[0] 703 704 # Validate certain fields. 705 706 title = form.get("title", [""])[0].strip() 707 template = form.get("template", [""])[0].strip() 708 parent = form.get("parent", [""])[0].strip() 709 710 if not title: 711 return 0, _("No event title specified.") 712 if not template: 713 return 0, _("No event template specified.") 714 715 try: 716 start_day = self._get_input(form, "start-day") 717 start_month = self._get_input(form, "start-month") 718 start_year = self._get_input(form, "start-year") 719 720 if not start_day or not start_month or not start_year: 721 return 0, _("A start date must be specified.") 722 723 end_day = self._get_input(form, "end-day", start_day) 724 end_month = self._get_input(form, "end-month", start_month) 725 end_year = self._get_input(form, "end-year", start_year) 726 727 except (TypeError, ValueError): 728 return 0, _("Days and years must be numbers yielding a valid date!") 729 730 try: 731 start_hour = self._get_input(form, "start-hour") 732 start_minute = self._get_input(form, "start-minute") 733 start_second = self._get_input(form, "start-second") 734 735 end_hour = self._get_input(form, "end-hour") 736 end_minute = self._get_input(form, "end-minute") 737 end_second = self._get_input(form, "end-second") 738 739 except (TypeError, ValueError): 740 return 0, _("Hours, minutes and seconds must be numbers yielding a valid time!") 741 742 start_date = DateTime( 743 (start_year, start_month, start_day, start_hour, start_minute, start_second, start_zone) 744 ) 745 start_date.constrain() 746 747 end_date = DateTime( 748 (end_year, end_month, end_day, end_hour, end_minute, end_second, end_zone) 749 ) 750 end_date.constrain() 751 752 # An elementary date ordering check. 753 754 if (start_date.as_date() != end_date.as_date() or start_date.has_time() and end_date.has_time()) and start_date > end_date: 755 start_date, end_date = end_date, start_date 756 757 event_details = { 758 "start" : str(start_date), "end" : str(end_date), 759 "title" : title, "summary" : title, 760 "description" : description, "location" : location, "link" : link, 761 "topics" : [topic for topic in topics if topic] 762 } 763 764 if latitude and longitude: 765 event_details["geo"] = latitude, longitude 766 767 # Copy the template. 768 769 template_page = PageEditor(request, template) 770 771 if not template_page.exists(): 772 return 0, _("Event template not available.") 773 774 # Use any parent page information. 775 776 full_title = getFullPageName(parent, title) 777 778 if page_name: 779 780 # Allow parameters in the page name. This permits a degree of 781 # interoperability with MonthCalendar. 782 783 page_name = page_name.replace("@PAGE@", request.page.page_name) 784 page_name = page_name.replace("@DATE@", str(start_date.as_date())) 785 page_name = page_name.replace("@STARTDATE@", str(start_date.as_date())) 786 page_name = page_name.replace("@ENDDATE@", str(end_date.as_date())) 787 page_name = page_name.replace("@PARENT@", parent) 788 page_name = page_name.replace("@TITLE@", title) 789 790 # Normalise any page hierarchy separators. 791 792 page_name = re.sub("/+", "/", page_name) 793 794 else: 795 page_name = full_title 796 797 # Load the new page and replace the event details in the body. 798 799 new_page = PageEditor(request, page_name) 800 801 if new_page.exists(): 802 return 0, _("The specified page already exists. Please choose another name.") 803 804 # Complete the new page and return its body. 805 806 body = fillEventPageFromTemplate(template_page, new_page, event_details, category_pagenames) 807 808 # Open the page editor on the new page. 809 # NOTE: Replacing the revision in the request to prevent Moin from 810 # NOTE: attempting to use the queued changes page's revision. 811 # NOTE: Replacing the action and page in the request to avoid issues 812 # NOTE: with editing tickets. 813 814 request.rev = 0 815 request.action = "edit" 816 request.page = new_page 817 new_page.sendEditor(preview=body, staytop=True) 818 819 # Return success. 820 821 return 1, None 822 823 # Action function. 824 825 def execute(pagename, request): 826 EventAggregatorNewEvent(pagename, request).render() 827 828 # vim: tabstop=4 expandtab shiftwidth=4