migration more code into git

This commit is contained in:
Stefan Kögl 2012-03-31 17:55:42 +02:00
parent 042b5ffd98
commit ea5649a2eb
28 changed files with 4609 additions and 0 deletions

15
TracBooking/design.txt Normal file
View File

@ -0,0 +1,15 @@
* Person:
* nick
* Vorname
* Nachname
* birthday
* bezahlt
* Ernährung
* carnivor, vegetarisch, vegan
Buchung:
* Frühstück
* Schlafplatz
* anwesende Tage
* Extrawürste (anmerkungen, Wünsche)

21
TracBooking/setup.py Normal file
View File

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from setuptools import find_packages, setup
setup(
name='TracBooking', version='0.1',
packages=['tracbooking',],
entry_points = """
[trac.plugins]
booking = tracbooking
""",
package_data={'tracbooking': ['templates/*.html',
'htdocs/css/*.css',
'htdocs/images/*']},
author = "Stefan Kögl",
author_email = "hotshelf@trac.ctdo.de",
description = "a plugin for event booking",
license = "GPL",
keywords = "register, events",
url = "http://trac.ctdo.de/dev/", # project home page, if any
)

View File

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
import model
import web_ui
import admin
import macros

View File

@ -0,0 +1,586 @@
# -*- coding: utf-8 -*-
import os
from os.path import join
from re import match
from trac.admin import IAdminPanelProvider
from trac.core import *
from trac.web.chrome import add_stylesheet, add_warning, add_ctxtnav, add_script, add_notice
from trac.web import RequestDone
from trac.util.translation import _, N_, gettext
from trac.util.datefmt import utc
from trac.util.text import to_unicode
from datetime import datetime
from tracbooking.model import *
from tracbooking.report import create_report
from tracbooking.utils import validate_id, time_parse, date_parse, get_tz, get_option_count, validate_email
from tracbooking.web_ui import UserUploadComponent
def get_actions(myactions):
actions = []
for action in myactions:
if isinstance(action, tuple):
actions.append(action[0])
else:
actions.append(action)
return actions
class BookingAdminPanel(Component):
implements(IAdminPanelProvider)
# IAdminPanelProvider methods
def get_admin_panels(self, req):
if 'BOOKING_ADMIN' in req.perm:
yield ('booking', _('Booking System'), 'reminder',
'Erinnerungen verwalten')
yield ('booking', _('Booking System'), 'events',
'Events verwalten')
yield ('booking', _('Booking System'), 'options',
'Optionen verwalten')
yield ('booking', _('Booking System'), 'attendees',
'Teilnehmer')
yield ('booking', _('Booking System'), 'options2events',
u'Optionen mit Events verknüpfen')
yield ('booking', _('Booking System'), 'einkauf',
u'Bestellungen einsehen')
def render_admin_panel(self, req, cat, page, path_info):
req.perm.require('BOOKING_ADMIN')
print "render", req, cat, page, path_info
if page == "events":
return self._render_events(req, cat, page, path_info)
if page == "attendees":
return self._render_attendees(req, cat, page, path_info)
if page == "options":
return self._render_options(req, cat, page, path_info)
if page == "options2events":
return self._render_options2events(req, cat, page, path_info)
if page == "reminder":
return self._render_reminder(req, cat, page, path_info)
if page == "einkauf":
return self._render_einkauf(req, cat, page, path_info)
#return self._render_admin_panel(req, cat, page, path_info)
def _render_reminder(self, req, cat, page, bookingtype):
print "BookingReminderManager._render_admin_panel()"
add_stylesheet (req, 'hw/css/booking.css')
key = req.path_info
m = match(r'/admin/booking/reminder/(\d+)$', key)
if not m:
if key == "/admin/booking/reminder":
return "admin_reminder.html", {"events" : Event.fetch_all(self.env), "reminders" : None}
e_id = int(m.group(1))
event = Event.fetch_one(self.env, e_id)
if req.method == "POST":
if req.args.get("add") and \
req.args.has_key("text") and \
req.args.has_key("notify_on"):
text = req.args.get("text")
notify_on = req.args.get("notify_on")
notify_on = time_parse(notify_on)
reminder = BookingReminder(self.env, 0, e_id, text, notify_on)
reminder.commit()
elif req.args.has_key("save") and req.args.has_key("sel"):
sel = req.args.get('sel')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
BookingReminder.delete(self.env, int(key))
return "admin_reminder.html", {"reminders" : BookingReminder.fetch_by_event(self.env, e_id), "eventname" : event.name}
def _render_events(self, req, cat, page, bookingtype):
add_stylesheet (req, 'hw/css/booking.css')
add_stylesheet (req, 'hw/css/ui.all.css')
add_script (req, 'hw/script/jquery-ui-1.6.custom.min.js')
key = req.path_info
data = {}
e_id = None
m = match(r'/admin/booking/events/(\d+)$', key)
if m:
e_id = int(m.group(1))
event = Event.fetch_one(self.env, e_id)
account = EventAccount.fetch_by_event(self.env, e_id)
else:
event = Event(self.env, 0, "", "", datetime.now(utc), datetime.now(utc))
account = EventAccount(self.env, 0, 0, "", "", "", "", "", "")
if req.method == "POST":
if req.args.get("add") and \
req.args.has_key("name") and \
req.args.has_key("date_begin") and \
req.args.has_key("date_end") and \
req.args.has_key("time_begin") and \
req.args.has_key("time_end"):
name = req.args.get("name")
date_begin = date_parse(req.args.get("date_begin"))
time_begin = time_parse(req.args.get("time_begin"))
dt_begin = datetime.combine(date_begin, time_begin)
dt_begin = dt_begin.replace(tzinfo=utc)
date_end = date_parse(req.args.get("date_end"))
time_end = time_parse(req.args.get("time_end"))
dt_end = datetime.combine(date_end, time_end)
dt_end = dt_end.replace(tzinfo=utc)
date_begin = date_parse(req.args.get("edit_deadline_date"))
time_begin = time_parse(req.args.get("edit_deadline_time"))
edit_deadline = datetime.combine(date_begin, time_begin)
edit_deadline = edit_deadline.replace(tzinfo=utc)
payment_deadline_date = date_parse(req.args.get("payment_deadline_date"))
payment_deadline_time = time_parse(req.args.get("payment_deadline_time"))
payment_deadline = datetime.combine(payment_deadline_date, payment_deadline_time)
payment_deadline = payment_deadline.replace(tzinfo=utc)
account_owner = req.args.get("account_owner")
account_no = req.args.get("account_no")
bank_name = req.args.get("bank_name")
bank_no = req.args.get("bank_no")
first_reason = req.args.get("first_reason")
#second_reason = req.args.get("second_reason")
description = req.args.get("description", "")
event.name = name
event.description = description
event.time_begin = dt_begin
event.time_end = dt_end
event.edit_deadline = edit_deadline
event.payment_deadline = payment_deadline
account.account_owner = account_owner
account.account_no = account_no
account.bank_name = bank_name
account.bank_no = bank_no
account.first_reason = first_reason
#account.second_reason = second_reason
if not e_id:
event.commit()
account.commit()
else:
event.update()
account.update()
elif req.args.has_key("save") and req.args.has_key("rsel"):
sel = req.args.get('rsel')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
e_id = int(key)
Event.delete(self.env, e_id)
elif req.args.has_key("save") and req.args.has_key("copy"):
e_id = int(req.args.get('copy'))
event = Event.copy(self.env, e_id)
elif req.args.has_key("save") and req.args.has_key("finish"):
e_id = int(req.args.get('finish'))
attendees = Attendee.fetch_all(self.env, e_id)
for attendee in attendees:
attendee.finished = True
attendee.update()
event = Event.fetch_one(self.env, e_id)
event.name += " (Abgeschlossen)"
event.update()
req.redirect(req.href("/admin/booking/events", e_id))
data["event"] = event
data["event_account"] = account
data["events"] = Event.fetch_all(self.env)
return "admin_events.html", data
def _render_options(self, req, cat, page, bookingtype):
print "AvailableOptionsManager._render_admin_panel()"
add_stylesheet (req, 'hw/css/booking.css')
data = {}
key = req.path_info
m = match(r'/admin/booking/options/(\d+)$', key)
option_id = None
option = AvailableOption(self.env, 0, "", "", 0.0, 0.0, 0, 0, 0, 0, 0)
if m:
option_id = int(m.group(1))
option = AvailableOption.fetch_one(self.env, option_id)
if req.method == "POST":
if req.args.get("add") and \
req.args.has_key("name") and \
req.args.has_key("price"):
name = req.args.get("name")
desc = req.args.get("desc")
price = float(req.args.get("price"))
minc = int(req.args.get("min_count", 0))
maxc = int(req.args.get("max_count", 0))
supplier_id = int(req.args.get("supplier_id", 0))
ext_id = req.args.get("ext_id", None)
stock_count = int(req.args.get("stock_count", 0))
option.name = name
option.price = price
option.description = desc
option.min_count = minc
option.max_count = maxc
option.supplier_id = supplier_id
option.ext_id = ext_id
option.stock_count = stock_count
if option_id:
option.update()
else:
option.commit()
req.redirect(req.href.admin("booking", "options"))
elif req.args.has_key("save"):
if req.args.has_key("rsel"):
sel = req.args.get('rsel')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
try:
ao_id = int(key)
except ValueError:
raise TracError("wrong value")
AvailableOption.delete(self.env, ao_id)
a_ids = set()
if req.args.has_key("actives"):
sel = req.args.get('actives')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
try:
a_ids.add(int(key))
except ValueError:
continue
options = AvailableOption.fetch_all(self.env)
for option in options:
if option.active:
if not option.ao_id in a_ids:
option.active = 0
option.update()
elif option.ao_id in a_ids:
option.active = 1
option.update()
data["options"] = AvailableOption.fetch_all(self.env)
data["suppliers"] = Supplier.fetch_all(self.env)
data["option"] = option
return "admin_options.html", data
def _render_attendees(self, req, cat, page, bookingtype):
print "RegistrationOverview._render_admin_panel()"
add_stylesheet (req, 'hw/css/booking.css')
key = req.path_info
m1 = match(r'/admin/booking/attendees/(\d+)/(\d+)$', key)
m2 = match(r'/admin/booking/attendees/(\d+)$', key)
print "data", req.args, cat, page, bookingtype
print "matches", m1, m2
if m1:
notice = "Bestellung erfolgreich gespeichert."
if req.session.has_key("notice"):
add_notice(req, req.session["notice"])
del req.session["notice"]
req.session.save()
event_id = int(m1.group(1))
a_id = m1.group(2)
attendee = Attendee.fetch_one(self.env, a_id, e_id=event_id, fetch_options=True)
add_ctxtnav(req, 'Back to Overview', req.href.admin("booking", "attendees", event_id))
data = {"attendee" : attendee,
"bank_name" : self.config.get("booking", "bank_name"),
"bank_no" : self.config.get("booking", "bank_no"),
"account_owner" : self.config.get("booking", "account_owner"),
"account" : self.config.get("booking", "account"),
"first_reason" : self.config.get("booking", "first_reason")}
print 1
if req.method == "POST":
print "post", attendee.finished
if not attendee.finished:
print "not finished"
failure = False
if req.args.has_key("email"):
email = req.args["email"]
if not validate_email(email):
add_warning(req, u"email nicht gültig")
attendee.email = email
attendee.update()
req.session["notice"] = "Daten erfolgreich aktualisiert."
elif req.args.has_key("unregister"):
UserUploadComponent(self.env).clean_userdir(attendee)
Attendee.delete(self.env, attendee.a_id)
print "redirect to", req.href.admin("booking", "attendees", event_id)
req.redirect(req.href.admin("booking", "attendees", event_id))
elif req.args.has_key("finish"):
attendee.finished = True
attendee.update()
print "redirect to", req.href.admin("booking", "attendees", event_id, a_id)
req.redirect(req.href.admin("booking", "attendees", event_id, a_id))
elif req.args.has_key("unfinish"):
attendee.finished = False
attendee.update()
print "redirect to", req.href.admin("booking", "attendees", event_id, a_id)
req.redirect(req.href.admin("booking", "attendees", event_id, a_id))
else:
args = req.args
for arg in args:
if arg.startswith("count"):
try:
prefix, ao_id = arg.split("_", 1)
ao_id = int(ao_id)
count = int(args[arg])
validate_id(count)
validate_id(ao_id)
except ValueError:
add_warning(req, u"Bitte für Anzahlfelder nur positive Zahen eingeben.")
failure = True
continue
aoption = AvailableOption.fetch_one(self.env, ao_id, fetch_variations=False)
if not aoption:
add_warning(req, u"Artikel %r nicht gefunden" % ao_id)
failure = True
continue
elif not aoption.active:
add_warning(req, u"Artikel %r nicht aktiviert" % ao_id)
failure = True
continue
if count < aoption.min_count:
add_warning(req, u"Artikel '%s' kann minimal '%d' Mal bestellt werden;-)" % (aoption.name, aoption.min_count))
failure = True
continue
elif aoption.max_count and count > aoption.max_count:
add_warning(req, u"Artikel '%s' kann maximal '%d' Mal bestellt werden;-)" % (aoption.name, aoption.max_count))
failure = True
continue
if not count:
BookingOption.delete(self.env, attendee.a_id, ao_id)
else:
opt = BookingOption.fetch_one(self.env, attendee.a_id, ao_id)
if not opt:
opt = BookingOption(self.env, 0, attendee.a_id, ao_id, count)
opt.commit()
else:
opt.count = count
opt.update()
#elif arg.startswith("var"):
#prefix, variation_id = arg.split("_", 1)
#try:
#variation_id = int(variation_id)
#validate_id(variation_id)
#value = int(args[arg])
#validate_id(value)
#except (ValueError,):
#add_warning(req, u"Bitte eine Zahl eingeben;-)")
#failure = True
#continue
#variation = BookingOptionVariation.fetch_one(self.env, attendee.a_id, variation_id)
#if not variation:
#b = BookingOptionVariation(self.env, attendee.a_id, variation_id, value)
#b.commit()
#else:
#BookingOptionVariation.update(self.env, attendee.a_id, variation_id, value)
if not failure:
req.session["notice"] = notice
print "before redirect", req.href.admin("booking", "attendees", event_id, a_id)
req.redirect(req.href.admin("booking", "attendees", event_id, a_id))
elif req.args.has_key("download_invoice"):
e_id = req.args["event_id"]
event = Event.fetch_one(self.env, e_id)
session_tzname, selected_tz = get_tz(req.session.get('tz', self.env.config.get("trac", "default_timezone") or None))
data = create_attendee_report(self.env, event, attendee, selected_tz)
data_len = len(data)
req.send_response(200)
req.send_header("Content-Type", "text/pdf;charset=utf-8")
req.send_header("Content-Length", data_len)
req.send_header("Content-Disposition", 'filename=%s.pdf' % event.name.replace("/", "_").replace(u" ", u"_"))
req.end_headers()
req.write(data)
raise RequestDone
elif req.args.has_key("unfinish"):
attendee.finished = False
attendee.update()
print "redirect to", req.href.admin("booking", "attendees", event_id, a_id)
req.redirect(req.href.admin("booking", "attendees", event_id, a_id))
else:
attendee = Attendee.fetch_one(self.env, a_id, e_id=event_id, fetch_options=True)
event = Event.fetch_one(self.env, e_id=event_id, fetch_options=True, attendee_id=attendee.a_id)
for i in event.options:
get_option_count(attendee, i)
data.update({"event" : event, "attendee" : attendee})
return 'admin_attendee_status_edit.html', data
if not m2:
#if key == "/admin/booking/attendees":
return "admin_attendees.html", {"events" : Event.fetch_all(self.env), "attendees" : None, "event" : None}
else:
add_ctxtnav(req, 'Back to Overview', req.href.admin("booking", "attendees"))
e_id = int(m2.group(1))
event = Event.fetch_one(self.env, e_id)
if req.method == "POST":
if req.args.has_key("remove_empty"):
attendees = Attendee.fetch_all(self.env, e_id=e_id, fetch_options=True)
for attendee in attendees:
if not attendee.options:
attendee.delete(self.env, attendee.a_id)
req.redirect(req.href.admin("booking", "attendees", e_id))
elif req.args.has_key("download_report"):
session_tzname, selected_tz = get_tz(req.session.get('tz', self.env.config.get("trac", "default_timezone") or None))
data = create_report(self.env, e_id, selected_tz)
data_len = len(data)
req.send_response(200)
req.send_header("Content-Type", "text/pdf;charset=utf-8")
req.send_header("Content-Length", data_len)
req.send_header("Content-Disposition", 'filename=%s.pdf' % event.name.replace("/", "_").replace(" ", "_"))
req.end_headers()
req.write(data)
raise RequestDone
if req.args.has_key("sel"):
sel = req.args.get('sel')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
try:
a_id = int(key)
except ValueError:
continue
else:
UserUploadComponent(self.env).clean_userdir(Attendee.fetch_one(self.env, a_id))
Attendee.delete(self.env, a_id)
attendees = Attendee.fetch_all(self.env, e_id, True)
a_ids = set()
if req.args.has_key("has_paid"):
sel = req.args.get('has_paid')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
try:
a_ids.add(int(key))
except ValueError:
continue
for attendee in attendees:
if attendee.has_paid:
if not attendee.a_id in a_ids:
attendee.has_paid = 0
attendee.update()
elif attendee.a_id in a_ids:
attendee.has_paid = 1
attendee.update()
actual_amount = req.args.get('actual_amount_%d' % attendee.a_id)
if actual_amount:
try:
attendee.actual_amount = float(actual_amount)
attendee.update()
except TypeError:
add_warning(req, u"Das Attribut 'Eingezahlter Betrag' muss ein float Wert sein")
else:
attendee.actual_amount = None
attendee.update()
return "admin_attendees.html", {"attendees" : Attendee.fetch_all(self.env, e_id, True), "event" : event, "events" : Event.fetch_all(self.env)}
def _render_options2events(self, req, cat, page, bookingtype):
print "ComponentOptions2EventsManager._render_admin_panel()"
add_stylesheet (req, 'hw/css/booking.css')
data = {}
if req.method == "POST":
if req.args.get("add"):
option = req.args.get("option")
event = req.args.get("event")
option = AvailableOption.fetch_one(self.env, name=option)
event = Event.fetch_one(self.env, name=event)
if not option or not event:
raise TracError(_(u'Nicht genügend Informationen'))
o2e = Option2Event.fetch_one(self.env, option.ao_id, event.e_id)
if o2e:
add_warning(req, _(u'Option bereits zum Event hinzugefügt'))
data.update({"options" : AvailableOption.fetch_all(self.env),
"events" : Event.fetch_all(self.env, fetch_options=True)})
return "admin_options_to_events.html", data
o2e = Option2Event(self.env, option.ao_id, event.e_id)
o2e.commit()
elif req.args.has_key("save") and req.args.has_key("sel"):
sel = req.args.get('sel')
if isinstance(sel, basestring):
sel = [sel,]
for key in sel:
ao_id, e_id = key.split(":", 1)
Option2Event.delete(self.env, int(ao_id), int(e_id))
data.update({"options" : AvailableOption.fetch_all(self.env),
"events" : Event.fetch_all(self.env, fetch_options=True)})
return "admin_options_to_events.html", data
def _render_einkauf(self, req, cat, page, bookingtype):
print "ComonentEinkauf._render_admin_panel()"
add_stylesheet (req, 'hw/css/booking.css')
key = req.path_info
m = match(r'/admin/booking/einkauf/(\d+)$', key)
if not m:
if key == "/admin/booking/einkauf":
return "admin_einkauf.html", {"events" : Event.fetch_all(self.env), "items" : None}
e_id = int(m.group(1))
e = Event.fetch_one(self.env, e_id)
if req.method == "POST":
if req.args.has_key("remove_empty"):
attendees = Attendee.fetch_all(self.env, e_id=e_id, fetch_options=True)
attendees = sorted(attendees, key=lambda x: x.nick)
for attendee in attendees:
if not attendee.options:
attendee.delete(self.env, attendee.a_id)
req.redirect(req.href.admin("booking", "einkauf", e_id))
elif req.args.has_key("download_report"):
session_tzname, selected_tz = get_tz(req.session.get('tz', self.env.config.get("trac", "default_timezone") or None))
data = create_report(self.env, e_id, selected_tz)
data_len = len(data)
req.send_response(200)
req.send_header('Content-Type', "text/pdf;charset=utf-8")
req.send_header('Content-Length', data_len)
req.send_header('Content-Disposition', 'filename=%s_bestellung.pdf' % e.name.replace("/", "_").replace(" ", "_"))
req.end_headers()
req.write(data)
raise RequestDone
data = {}
query_old = "SELECT booking_option.ao_id,booking_available_option.name,booking_available_option.price," \
"SUM(booking_option.count),booking_available_option.price * SUM(booking_option.count)" \
"FROM booking_available_option,booking_option,option_to_event " \
"WHERE booking_available_option.ao_id = booking_option.ao_id AND " \
"booking_available_option.ao_id = option_to_event.ao_id AND " \
"option_to_event.e_id=%s GROUP BY booking_option.ao_id;"
query = "select " \
"booking_option.ao_id, " \
"booking_available_option.name, " \
"booking_available_option.price, " \
"SUM(booking_option.count), " \
"SUM(booking_option.count) * booking_available_option.price " \
"from " \
"booking_option,booking_available_option " \
"where " \
"booking_option.a_id IN (select a_id from attendee where e_id=%s) AND " \
"booking_option.ao_id = booking_available_option.ao_id " \
"group by " \
"booking_option.ao_id;"
db = self.env.get_db_cnx()
cursor = db.cursor()
cursor.execute(query, (e_id,))
items = cursor.fetchall()
return "admin_einkauf.html", {"events" : Event.fetch_all(self.env), "items" : items, "eventname": e.name}

View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-

View File

@ -0,0 +1,8 @@
.booking-block, fieldset, fieldset.register {text-align:center;display:inline-block;border:2px solid #333;-moz-border-radius:17px;-khtml-border-radius:17px;-webkit-border-radius:17px;margin:10px;}
.admin-block {border:2px solid #333;-moz-border-radius:17px;-khtml-border-radius:17px;-webkit-border-radius:17px;margin:10px;padding:10px;}
#booking-bank, #booking-options, #event_details, #booking-attendee {padding:10px;display:block;text-align:left;}
#booking-options table {border:1px solid #000;margin-bottom:10px;margin:0px!;padding:0px!;}
#booking-options td, #booking-options th {border:1px solid #000;margin:0px!;padding:0px!;}
#booking-options th {font-weight:bold;padding:0px!;margin:0px!;}
#booking-options td {text-align:left;padding:5px;}
#payment-deadline-data, #edit-deadline-data {font-weight:bold;color:red;}

View File

@ -0,0 +1,609 @@
/*
* jQuery UI screen structure and presentation
* This CSS file was generated by ThemeRoller, a Filament Group Project for jQuery UI
* Author: Scott Jehl, scott@filamentgroup.com, http://www.filamentgroup.com
* Visit ThemeRoller.com
*/
/*
* Note: If your ThemeRoller settings have a font size set in ems, your components will scale according to their parent element's font size.
* As a rule of thumb, set your body's font size to 62.5% to make 1em = 10px.
* body {font-size: 62.5%;}
*/
/*UI accordion*/
.ui-accordion {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
font-size: 1.1em;
}
.ui-accordion-group {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
border: 1px solid #cccccc;
border-bottom: none;
}
.ui-accordion-header {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
cursor: pointer;
background: #f6f6f6;
}
.ui-accordion-header a {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
display: block;
font-size: 1em;
font-weight: bold;
text-decoration: none;
padding: .5em .5em .5em 1.7em;
color: #1c94c4;
background: url(images/ef8c08_7x7_arrow_right.gif) .5em 50% no-repeat;
}
.ui-accordion-header a:hover {
background: url(images/ef8c08_7x7_arrow_right.gif) .5em 50% no-repeat;
color: #c77405;
}
.ui-accordion-header:hover {
background: #fdf5ce;
color: #c77405;
}
.selected .ui-accordion-header {background: #004;}
.selected .ui-accordion-header a, .selected .ui-accordion-header a:hover {
color: #eb8f00;
background: url(images/ef8c08_7x7_arrow_down.gif) .5em 50% no-repeat;
}
.ui-accordion-content {
/* background: #eeeeee url(images/eeeeee_40x100_textures_03_highlight_soft_100.png) 0 0 repeat-x; */
background: #eeeeee;
color: #333333;
font-size: 1em;
}
.ui-accordion-content p {
padding: 1em 1.7em 0.6em;
}
/*UI tabs*/
.ui-tabs-nav {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
font-size: 1.1em;
float: left;
position: relative;
z-index: 1;
border-right: 1px solid #cccccc;
bottom: -1px;
}
.ui-tabs-nav ul {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
}
.ui-tabs-nav li {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
float: left;
border: 1px solid #cccccc;
border-right: none;
}
.ui-tabs-nav li a {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
float: left;
font-size: 1em;
font-weight: bold;
text-decoration: none;
padding: .5em 1.7em;
color: #1c94c4;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
}
.ui-tabs-nav li a:hover {
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
color: #c77405;
}
.ui-tabs-nav li.ui-tabs-selected {
border-bottom-color: #ffffff;
}
.ui-tabs-nav li.ui-tabs-selected a, .ui-tabs-nav li.ui-tabs-selected a:hover {
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
color: #eb8f00;
}
.ui-tabs-panel {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
clear:left;
border: 1px solid #cccccc;
background: #eeeeee url(images/eeeeee_40x100_textures_03_highlight_soft_100.png) 0 0 repeat-x;
color: #333333;
padding: 1.5em 1.7em;
font-size: 1.1em;
}
.ui-tabs-hide {
display: none;/* for accessible hiding: position: absolute; left: -99999999px*/;
}
/*slider*/
.ui-slider {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
font-size: 1.1em;
background: #eeeeee url(images/eeeeee_40x100_textures_03_highlight_soft_100.png) 0 0 repeat-x;
border: 1px solid #dddddd;
height: .8em;
position: relative;
}
.ui-slider-handle {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
position: absolute;
z-index: 2;
top: -3px;
width: 1.2em;
height: 1.2em;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
border: 1px solid #cccccc;
}
.ui-slider-handle:hover {
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
border: 1px solid #fbcb09;
}
.ui-slider-handle-active, .ui-slider-handle-active:hover {
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
border: 1px solid #fbd850;
}
.ui-slider-range {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
height: .8em;
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
position: absolute;
border: 1px solid #cccccc;
border-left: 0;
border-right: 0;
top: -1px;
z-index: 1;
opacity:.7;
filter:Alpha(Opacity=70);
}
/*dialog*/
.ui-dialog {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
font-size: 1.1em;
background: #eeeeee url(images/eeeeee_40x100_textures_03_highlight_soft_100.png) 0 0 repeat-x;
color: #333333;
border: 4px solid #dddddd;
position: relative;
}
.ui-resizable-handle {
position: absolute;
font-size: 0.1px;
z-index: 99999;
}
.ui-resizable .ui-resizable-handle {
display: block;
}
body .ui-resizable-disabled .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
body .ui-resizable-autohide .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
.ui-resizable-n {
cursor: n-resize;
height: 7px;
width: 100%;
top: -5px;
left: 0px;
}
.ui-resizable-s {
cursor: s-resize;
height: 7px;
width: 100%;
bottom: -5px;
left: 0px;
}
.ui-resizable-e {
cursor: e-resize;
width: 7px;
right: -5px;
top: 0px;
height: 100%;
}
.ui-resizable-w {
cursor: w-resize;
width: 7px;
left: -5px;
top: 0px;
height: 100%;
}
.ui-resizable-se {
cursor: se-resize;
width: 13px;
height: 13px;
right: 0px;
bottom: 0px;
background: url(images/222222_11x11_icon_resize_se.gif) no-repeat 0 0;
}
.ui-resizable-sw {
cursor: sw-resize;
width: 9px;
height: 9px;
left: 0px;
bottom: 0px;
}
.ui-resizable-nw {
cursor: nw-resize;
width: 9px;
height: 9px;
left: 0px;
top: 0px;
}
.ui-resizable-ne {
cursor: ne-resize;
width: 9px;
height: 9px;
right: 0px;
top: 0px;
}
.ui-dialog-titlebar {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
padding: .5em 1.5em .5em 1em;
color: #1c94c4;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
border-bottom: 1px solid #cccccc;
font-size: 1em;
font-weight: bold;
position: relative;
}
.ui-dialog-title {}
.ui-dialog-titlebar-close {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
background: url(images/ef8c08_11x11_icon_close.gif) 0 0 no-repeat;
position: absolute;
right: 8px;
top: .7em;
width: 11px;
height: 11px;
z-index: 100;
}
.ui-dialog-titlebar-close-hover, .ui-dialog-titlebar-close:hover {
background: url(images/ef8c08_11x11_icon_close.gif) 0 0 no-repeat;
}
.ui-dialog-titlebar-close:active {
background: url(images/ef8c08_11x11_icon_close.gif) 0 0 no-repeat;
}
.ui-dialog-titlebar-close span {
display: none;
}
.ui-dialog-content {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
color: #333333;
padding: 1.5em 1.7em;
}
.ui-dialog-buttonpane {
position: absolute;
bottom: 0;
width: 100%;
text-align: left;
border-top: 1px solid #dddddd;
background: #eeeeee;
}
.ui-dialog-buttonpane button {
margin: .5em 0 .5em 8px;
color: #1c94c4;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
font-size: 1em;
border: 1px solid #cccccc;
cursor: pointer;
padding: .2em .6em .3em .6em;
line-height: 1.4em;
}
.ui-dialog-buttonpane button:hover {
color: #c77405;
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
border: 1px solid #fbcb09;
}
.ui-dialog-buttonpane button:active {
color: #eb8f00;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
border: 1px solid #fbd850;
}
/* This file skins dialog */
.ui-dialog.ui-draggable .ui-dialog-titlebar,
.ui-dialog.ui-draggable .ui-dialog-titlebar {
cursor: move;
}
/*datepicker*/
/* Main Style Sheet for jQuery UI date picker */
.ui-datepicker-div, .ui-datepicker-inline, #ui-datepicker-div {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
background: #eeeeee url(images/eeeeee_40x100_textures_03_highlight_soft_100.png) 0 0 repeat-x;
font-size: 1.1em;
border: 4px solid #dddddd;
width: 15.5em;
padding: 2.5em .5em .5em .5em;
position: relative;
}
.ui-datepicker-div, #ui-datepicker-div {
z-index: 9999; /*must have*/
display: none;
}
.ui-datepicker-inline {
float: left;
display: block;
}
.ui-datepicker-control {
display: none;
}
.ui-datepicker-current {
display: none;
}
.ui-datepicker-next, .ui-datepicker-prev {
position: absolute;
left: .5em;
top: .5em;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
}
.ui-datepicker-next {
left: 14.6em;
}
.ui-datepicker-next:hover, .ui-datepicker-prev:hover {
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
}
.ui-datepicker-next a, .ui-datepicker-prev a {
text-indent: -999999px;
width: 1.3em;
height: 1.4em;
display: block;
font-size: 1em;
background: url(images/ef8c08_7x7_arrow_left.gif) 50% 50% no-repeat;
border: 1px solid #cccccc;
cursor: pointer;
}
.ui-datepicker-next a {
background: url(images/ef8c08_7x7_arrow_right.gif) 50% 50% no-repeat;
}
.ui-datepicker-prev a:hover {
background: url(images/ef8c08_7x7_arrow_left.gif) 50% 50% no-repeat;
}
.ui-datepicker-next a:hover {
background: url(images/ef8c08_7x7_arrow_right.gif) 50% 50% no-repeat;
}
.ui-datepicker-prev a:active {
background: url(images/ef8c08_7x7_arrow_left.gif) 50% 50% no-repeat;
}
.ui-datepicker-next a:active {
background: url(images/ef8c08_7x7_arrow_right.gif) 50% 50% no-repeat;
}
.ui-datepicker-header select {
border: 1px solid #cccccc;
color: #1c94c4;
background: #f6f6f6;
font-size: 1em;
line-height: 1.4em;
position: absolute;
top: .5em;
margin: 0 !important;
}
.ui-datepicker-header option:focus, .ui-datepicker-header option:hover {
background: #fdf5ce;
}
.ui-datepicker-header select.ui-datepicker-new-month {
width: 7em;
left: 2.2em;
}
.ui-datepicker-header select.ui-datepicker-new-year {
width: 5em;
left: 9.4em;
}
table.ui-datepicker {
width: 15.5em;
text-align: right;
}
table.ui-datepicker td a {
padding: .1em .3em .1em 0;
display: block;
color: #1c94c4;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
cursor: pointer;
border: 1px solid #eeeeee;
}
table.ui-datepicker td a:hover {
border: 1px solid #fbcb09;
color: #c77405;
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
}
table.ui-datepicker td a:active {
border: 1px solid #fbd850;
color: #eb8f00;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
}
table.ui-datepicker .ui-datepicker-title-row td {
padding: .3em 0;
text-align: center;
font-size: .9em;
color: #333333;
text-transform: uppercase;
}
table.ui-datepicker .ui-datepicker-title-row td a {
color: #333333;
}
.ui-datepicker-cover {
display: none;
display/**/: block;
position: absolute;
z-index: -1;
filter: mask();
top: -4px;
left: -4px;
width: 193px;
height: 200px;
}
/*
Generic ThemeRoller Classes
>> Make your jQuery Components ThemeRoller-Compatible!
*/
/*component global class*/
.ui-component {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif;
font-size: 1.1em;
}
/*component content styles*/
.ui-component-content {
border: 1px solid #dddddd;
background: #eeeeee url(images/eeeeee_40x100_textures_03_highlight_soft_100.png) 0 0 repeat-x;
color: #333333;
}
.ui-component-content a {
color: #333333;
text-decoration: underline;
}
/*component states*/
.ui-default-state {
border: 1px solid #cccccc;
background: #f6f6f6 url(images/f6f6f6_40x100_textures_02_glass_100.png) 0 50% repeat-x;
font-weight: bold;
color: #1c94c4 !important;
}
.ui-default-state a {
color: #1c94c4;
}
.ui-default-state:hover, .ui-hover-state {
border: 1px solid #fbcb09;
background: #fdf5ce url(images/fdf5ce_40x100_textures_02_glass_100.png) 0 50% repeat-x;
font-weight: bold;
color: #c77405 !important;
}
.ui-hover-state a {
color: #c77405;
}
.ui-default-state:active, .ui-active-state {
border: 1px solid #fbd850;
background: #ffffff url(images/ffffff_40x100_textures_02_glass_65.png) 0 50% repeat-x;
font-weight: bold;
color: #eb8f00 !important;
outline: none;
}
.ui-active-state a {
color: #eb8f00;
outline: none;
}
/*icons*/
.ui-arrow-right-default {background: url(images/ef8c08_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-right-default:hover, .ui-arrow-right-hover {background: url(images/ef8c08_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-right-default:active, .ui-arrow-right-active {background: url(images/ef8c08_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-right-content {background: url(images/222222_7x7_arrow_right.gif) no-repeat 50% 50%;}
.ui-arrow-left-default {background: url(images/ef8c08_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-left-default:hover, .ui-arrow-left-hover {background: url(images/ef8c08_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-left-default:active, .ui-arrow-left-active {background: url(images/ef8c08_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-left-content {background: url(images/222222_7x7_arrow_left.gif) no-repeat 50% 50%;}
.ui-arrow-down-default {background: url(images/ef8c08_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-down-default:hover, .ui-arrow-down-hover {background: url(images/ef8c08_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-down-default:active, .ui-arrow-down-active {background: url(images/ef8c08_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-down-content {background: url(images/222222_7x7_arrow_down.gif) no-repeat 50% 50%;}
.ui-arrow-up-default {background: url(images/ef8c08_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-arrow-up-default:hover, .ui-arrow-up-hover {background: url(images/ef8c08_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-arrow-up-default:active, .ui-arrow-up-active {background: url(images/ef8c08_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-arrow-up-content {background: url(images/222222_7x7_arrow_up.gif) no-repeat 50% 50%;}
.ui-close-default {background: url(images/ef8c08_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-close-default:hover, .ui-close-hover {background: url(images/ef8c08_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-close-default:active, .ui-close-active {background: url(images/ef8c08_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-close-content {background: url(images/ef8c08_11x11_icon_close.gif) no-repeat 50% 50%;}
.ui-folder-closed-default {background: url(images/ef8c08_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-closed-default:hover, .ui-folder-closed-hover {background: url(images/ef8c08_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-closed-default:active, .ui-folder-closed-active {background: url(images/ef8c08_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-closed-content {background: url(images/ef8c08_11x11_icon_folder_closed.gif) no-repeat 50% 50%;}
.ui-folder-open-default {background: url(images/ef8c08_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-folder-open-default:hover, .ui-folder-open-hover {background: url(images/ef8c08_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-folder-open-default:active, .ui-folder-open-active {background: url(images/ef8c08_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-folder-open-content {background: url(images/ef8c08_11x11_icon_folder_open.gif) no-repeat 50% 50%;}
.ui-doc-default {background: url(images/ef8c08_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-doc-default:hover, .ui-doc-hover {background: url(images/ef8c08_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-doc-default:active, .ui-doc-active {background: url(images/ef8c08_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-doc-content {background: url(images/222222_11x11_icon_doc.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-default {background: url(images/ef8c08_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-default:hover, .ui-arrows-leftright-hover {background: url(images/ef8c08_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-default:active, .ui-arrows-leftright-active {background: url(images/ef8c08_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-leftright-content {background: url(images/222222_11x11_icon_arrows_leftright.gif) no-repeat 50% 50%;}
.ui-arrows-updown-default {background: url(images/ef8c08_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-arrows-updown-default:hover, .ui-arrows-updown-hover {background: url(images/ef8c08_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-arrows-updown-default:active, .ui-arrows-updown-active {background: url(images/ef8c08_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-arrows-updown-content {background: url(images/222222_11x11_icon_arrows_updown.gif) no-repeat 50% 50%;}
.ui-minus-default {background: url(images/ef8c08_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-minus-default:hover, .ui-minus-hover {background: url(images/ef8c08_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-minus-default:active, .ui-minus-active {background: url(images/ef8c08_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-minus-content {background: url(images/222222_11x11_icon_minus.gif) no-repeat 50% 50%;}
.ui-plus-default {background: url(images/ef8c08_11x11_icon_plus.gif) no-repeat 50% 50%;}
.ui-plus-default:hover, .ui-plus-hover {background: url(images/ef8c08_11x11_icon_plus.gif) no-repeat 50% 50%;}
.ui-plus-default:active, .ui-plus-active {background: url(images/ef8c08_11x11_icon_plus.gif) no-repeat 50% 50%;}
.ui-plus-content {background: url(images/222222_11x11_icon_plus.gif) no-repeat 50% 50%;}
/*hidden elements*/
.ui-hidden {
display: none;/* for accessible hiding: position: absolute; left: -99999999px*/;
}
.ui-accessible-hidden {
position: absolute; left: -99999999px;
}
/*reset styles*/
.ui-reset {
/*resets*/margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none;
}
/*clearfix class*/
.ui-clearfix:after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.ui-clearfix {display: inline-block;}
/* Hides from IE-mac \*/
* html .ui-clearfix {height: 1%;}
.ui-clearfix {display: block;}
/* End hide from IE-mac */
/* Note: for resizable styles, use the styles listed above in the dialog section */

View File

@ -0,0 +1,63 @@
<script language="JavaScript">
function countdown(year, month, day, hour, minute, second)
{
var end_date = new Date(year, month-1, day, hour, minute, second);
start_date = new Date();
if (end_date < end_date)
{
var years = 0, months = 0, days = 0, hours = 0, minutes = 0, seconds = 0;
// Jahre
while(end_date<end_date)
{
years++;
end_date.setFullYear(end_date.getFullYear()+1);
}
end_date.setFullYear(end_date.getFullYear()-1);
years--;
// Monate
while(end_date<end_date)
{
months++;
end_date.setMonth(end_date.getMonth()+1);
}
end_date.setMonth(end_date.getMonth()-1);
months--;
// Tage
while(end_date.getTime()+(24*60*60*1000)<end_date)
{
days++;
end_date.setTime(end_date.getTime()+(24*60*60*1000));
}
// Stunden
hours=Math.floor((end_date-end_date)/(60*60*1000));
end_date.setTime(end_date.getTime()+hours*60*60*1000);
// Minuten
minutes=Math.floor((end_date-end_date)/(60*1000));
end_date.setTime(end_date.getTime()+minutes*60*1000);
// Sekunden
seconds=Math.floor((end_date-end_date)/1000);
// Anzeige formatieren
(years!=1)?years=years+" Jahre, ":years=years+" Jahr, ";
(months!=1)?months=months+" Monate, ":months=months+" Monat, ";
(days!=1)?days=days+" Tage, ":days=days+" Tag, ";
(hours!=1)?hours=hours+" Stunden, ":hours=hours+" Stunde, ";
(minutes!=1)?minutes=minutes+" Minuten und ":minutes=minutes+" Minute und ";
if(seconds<10) seconds="0"+seconds;
(seconds!=1)?seconds=seconds+" Sekunden":seconds=seconds+" Sekunde";
document.countdownform.countdowninput.value= years+months+days+hours+minutes+seconds;
setTimeout('countdown()',200);
}
// Anderenfalls alles auf Null setzen
else
document.countdownform.countdowninput.value= "0 Jahre, 0 Monate, 0 Tage, 0 Stunden, 0 Minuten und 00 Sekunden";
}
</script>

View File

@ -0,0 +1,42 @@
<script type='text/javascript'>
<!--
var now = new Date();
var year = now.getYear();
if (year < 1900) {
year += 1900;
}
var end = new Date("July 17, " + year + " 23:00:00 GMT");
document.write('<div style="text-align: center">');
document.write(' <span id="c1" style="FONT: bold 30px arial; COLOR: green"></span><br />');
document.write(' <span id="c2" style="font: bold 25px arial; color: blue">;</span><br />');
document.write(' ...bis zum 31. Dezember ' + year);
document.write('</div>');
function toSt2(n) {
s = "";
if (n < 10) s += "0";
return (s + n).toString();
}
function toSt3(n) {
s = "";
if (n < 10) s += "00";
else if (n < 100) s += "0";
return (s + n).toString();
}
function countdown() {
d = new Date();
count = Math.floor(end.getTime() - d.getTime());
if(count > 0) {
miliseconds = toSt3(count%1000); count = Math.floor(count/1000);
seconds = toSt2(count%60); count = Math.floor(count/60);
minutes = toSt2(count%60); count = Math.floor(count/60);
hours = toSt2(count%24); count = Math.floor(count/24);
days = count;
document.getElementById('c1').innerHTML = days + ' TAGE';
document.getElementById('c2').innerHTML = hours + ':' + minutes + ':' + seconds + '.' + miliseconds + '';
setTimeout("countdown()", 50);
}
}
//-->
</script>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
from genshi.builder import tag
from genshi import Markup
from trac.wiki.macros import WikiMacroBase
from trac.util.translation import _
from trac.web.chrome import add_script
from datetime import datetime, timedelta
from pkg_resources import resource_filename
class CountDownMacro(WikiMacroBase):
"""Renders an overview of active RendezVouses"""
revision = "$Rev$"
url = "$URL$"
def expand_macro(self, formatter, name, content):
try:
event_datetime = datetime(*tuple(time.strptime("2010, 07, 16", "%Y, %m, %d"))[:3])
except Exception:
return ""
dt = event_datetime - datetime.now()
hours = "%d:%d" % (dt.seconds / 3600, (dt.seconds % 3600) / 60)
days = str(dt.days)
return Markup("""
<script type='text/javascript'>
<!--
var now = new Date();
var year = now.getYear();
if (year < 1900) {
year += 1900;
}
var end = new Date(2009, 6, 17, 17, 0, 0);
function toSt2(n) {
s = "";
if (n < 10) s += "0";
return (s + n).toString();
}
function toSt3(n) {
s = "";
if (n < 10) s += "00";
else if (n < 100) s += "0";
return (s + n).toString();
}
function countdown() {
d = new Date();
count = Math.floor(end.getTime() - d.getTime());
if(count > 0) {
miliseconds = toSt3(count%%1000); count = Math.floor(count/1000);
seconds = toSt2(count%%60); count = Math.floor(count/60);
minutes = toSt2(count%%60); count = Math.floor(count/60);
hours = toSt2(count%%24); count = Math.floor(count/24);
days = count;
document.getElementById('c1').innerHTML = days + ' TAGE';
document.getElementById('c2').innerHTML = hours + ':' + minutes + ':' + seconds + '.' + miliseconds + '';
setTimeout("countdown()", 50);
}
}
//-->
</script>
<div style="border:2px solid #fff;padding:10px;-moz-border-radius:17px;-khtml-border-radius:17px;-webkit-border-radius:17px;text-align:center;display:inline-block;">
<h2>CountDown</h2>
<span id="c1" style="font: bold 30px arial; border:1px solid #000; color:#fff;margin:5px;"> %s Tage</span>
<span id="c2" style="font: bold 25px arial; border:1px solid #000; color:#fff;margin:5px;"> %s </span>
<script language="javascript" type="text/javascript">countdown();</script>
</div>""" % (days, hours))

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-
import os
from datetime import datetime
from trac.util.datefmt import utc
from tracbooking.model import Attendee, Event, EventAccount
from tracbooking.utils import get_option_count
def get_file():
import codecs
return codecs.open(u"/tmp/printout.txt", "wr", "utf-8")
def add_doc(global_event, selected_tz, f):
#f.write(u".. header::\n")
#f.write(u" ###Page###\n")
#f.write(u" ###Title###\n")
dt = selected_tz.fromutc(datetime.now(utc)).strftime("%d.%m.%Y %H:%M")
d = u"Erstellt am: %s\n\n" % dt
f.write(d)
f.write(u"%s\n" % global_event.name)
f.write(u"%s\n\n" % ("=" * len(global_event.name)))
def create_table(items, f):
l = list()
l.append((u"Artikel", u"Einzelpreis", u"Anzahl", u"Gesamtpreis"))
for i in items:
l.append((u"%s" % i[1], u"%.2f" % float(i[2]), u"%s" % i[3], u"%.2f" % float(i[4])))
widths = list()
for cell in xrange(4):
tmp = list()
for row in l:
tmp.append(len(row[cell]))
widths.append(max(tmp))
head = list()
for ix, j in enumerate(l[0]):
head.append(u" %s " % j.ljust(widths[ix]))
trenner = [u"=%s=" % (u"=" * i)
for i in widths]
trenner = u" ".join(trenner) + "\n"
f.write(trenner)
f.write(u" ".join(head) + "\n")
f.write(trenner)
for i in l[1:]:
t = list()
for ix, j in enumerate(i):
t.append(u" " + j.ljust(widths[ix]) + " ")
f.write(u" ".join(t) + u"\n")
indent = sum(widths[:3]) + 10
f.write(u"%s %s\n" % (
" Gesamt ".center(indent),
str("%.2f" % float(sum([item[4] for item in items]))).ljust(widths[3])))
f.write(u"%s %s\n\n" % (u"=" * indent, u"=" * (widths[3] + 2)))
f.write(u"\n")
def add_summary(env, cursor, global_event, selected_tz, f):
query_old = "SELECT booking_option.ao_id,booking_available_option.name,booking_available_option.price," \
"SUM(booking_option.count),booking_available_option.price * SUM(booking_option.count)" \
"FROM booking_available_option,booking_option,option_to_event " \
"WHERE booking_available_option.ao_id = booking_option.ao_id AND " \
"booking_available_option.ao_id = option_to_event.ao_id AND " \
"option_to_event.e_id=%s GROUP BY booking_option.ao_id;"
query = "select " \
"booking_option.ao_id, " \
"booking_available_option.name, " \
"booking_available_option.price, " \
"SUM(booking_option.count), " \
"SUM(booking_option.count) * booking_available_option.price " \
"from " \
"booking_option,booking_available_option " \
"where " \
"booking_option.a_id IN (select a_id from attendee where e_id=%s) AND " \
"booking_option.ao_id = booking_available_option.ao_id " \
"group by " \
"booking_option.ao_id;"
cursor.execute(query, (global_event.e_id,))
items = cursor.fetchall()
create_table(items, f)
def add_payment_info(attendee, account, f):
txt = "Unser Konto"
f.write(u"%s\n%s\n\n" % (txt, u"-" * len(txt)))
f.write(u" :Kontoinhaber: %s\n" % account.account_owner)
f.write(u" :Kontonummer: %s\n" % account.account_no)
f.write(u" :Blz: %s\n" % account.bank_no)
f.write(u" :Bank: %s\n" % account.bank_name)
f.write(u" :Betrag: %s\n" % attendee.calculate_fee())
f.write(u" :1. Überweisungszweck: %s\n" % account.first_reason)
f.write(u" :2. Überweisungszweck: %s\n" % ("%X" % attendee.a_id).rjust(4, "0"))
f.write(u"\n")
def add_internal_attendee_data(attendee, f):
actual_amount = attendee.actual_amount and attendee.actual_amount or 0.0
f.write(u" :ID: %s\n" % ("%X" % attendee.a_id).rjust(4, "0"))
f.write(u" :Email: %s\n" % attendee.email)
f.write(u" :Soll-Betrag: %s\n" % attendee.calculate_fee())
f.write(u" :Ist-Betrag: %f\n" % actual_amount)
f.write(u" :Bezahlt: %s\n" % (u"Ja" if bool(attendee.has_paid) else u"Nein"))
f.write(u"\n")
def add_attendee(global_event, attendee, f, internal=False):
global_event.add_options(attendee.a_id)
f.write(u"%s\n" % attendee.nick.replace("_", "\_"))
f.write(u"%s\n" % ("-" * len(attendee.nick)))
if internal:
add_internal_attendee_data(attendee, f)
items = []
for option in global_event.options:
get_option_count(attendee, option)
if not option.count:
continue
items.append([option.ao_id, option.name, option.price, option.count, option.price * option.count])
if items:
create_table(items, f)
def add_attendees(env, cursor, global_event, selected_tz, f, internal=False):
attendees = Attendee.fetch_all(env, e_id=global_event.e_id, fetch_options=True)
attendees = sorted(attendees, key=lambda x: x.nick)
for attendee in attendees:
add_attendee(global_event, attendee, f, internal)
def generate_pdf(f):
import rst2pdf.createpdf
f.close()
rst2pdf.createpdf.main(["/tmp/printout.txt", "-o" , "/tmp/printout.pdf"])
os.remove("/tmp/printout.txt")
tmp = open("/tmp/printout.pdf").read()
os.remove("/tmp/printout.pdf")
return tmp
def create_report(env, e_id, selected_tz):
db = env.get_db_cnx()
cursor = db.cursor()
event = Event.fetch_one(env, e_id=e_id)
f = get_file()
add_doc(event, selected_tz, f)
add_summary(env, cursor, event, selected_tz, f)
add_attendees(env, cursor, event, selected_tz, f, True)
return generate_pdf(f)
def create_attendee_report(env, event, attendee, selected_tz):
db = env.get_db_cnx()
cursor = db.cursor()
f = get_file()
add_doc(event, selected_tz, f)
my_attendee = Attendee.fetch_one(env, a_id=attendee.a_id, e_id=event.e_id, fetch_options=True)
account = EventAccount.fetch_by_event(env, event.e_id)
add_payment_info(my_attendee, account, f)
add_attendee(event, my_attendee, f)
return generate_pdf(f)

View File

@ -0,0 +1,91 @@
<!DOCTYPE htm
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="admin.html" />
<head>
<script type="text/javascript">
$(document).ready(function() {
$('input[name^="count"], input[name="email"]').change(function() {
$(this).closest("form").submit();
});
});
</script>
<title>Anmeldungsstatus f&uuml;r ${event.name}</title>
</head>
<body>
<div id="content" py:with="fee = attendee.calculate_fee()">
<div id="booking-attendee" class="booking-block">
<h2>Anmeldungsdaten von ${attendee.nick}</h2>
<form name="options" uri="#anmeldungsdaten" method="post" mime-type="text/plain" action="">
<table class="properties" cellspacing="10px">
<tr>
<th id="h_author">Nick</th>
<td headers="h_author">${attendee.nick}</td>
</tr>
<tr>
<th id="h_email">Email</th>
<td headers="h_email"><input name="email" size="50" value="${attendee.email}"/></td>
</tr>
<tr>
<th id="h_paid">Bezahlt</th>
<td headers="h_paid">${bool(attendee.has_paid) and "Ja" or "Nein"}</td>
</tr>
</table>
<div class="mybuttons">
<input type="reset" value="Reset"/>
<input py:if="attendee.finished" type="submit" name="download_invoice" value="Abrechnung herunterladen"/>
<input py:if="not attendee.finished" type="submit" name="finish" value="Bestellung abschliessen"/>
<input py:if="attendee.finished" type="submit" name="unfinish" value="Bestellung wieder freischalten"/>
<input py:if="not attendee.finished" type="submit" name="unregister" value="Komplette Bestellung löschen"/>
<input type="submit" name="attendee-save" value="Speichern"/>
</div>
</form>
</div>
<div id='booking-options' class="booking-block">
<py:choose test="">
<py:when test="not attendee.finished">
<h2 id="available-options">Verfügbare Artikel</h2>
<form id="options" name="options" method="post" mime-type="text/plain" action="">
<table cellspacing="0px" cellpadding="0xp">
<tr>
<th>Artikel</th><th>Beschreibung</th><th>Preis in &euro; inkl. MwSt.</th><th>Anzahl</th>
</tr>
<tr py:for="option in event.options">
<td>${option.name}</td>
<td>${wiki_to_html(context, option.description)}</td>
<td id="price">${"%.2f" % option.price}</td>
<td>
<input id="count_${option.ao_id}" type="text" size="5" maxlength="4" name="count_${option.ao_id}" value="${option.count and option.count or 0}"/>
</td>
</tr>
<tr><td colspan="3" align="left"><b>Summe:</b></td><td>&euro; ${fee}</td></tr>
</table>
<div class="mybuttons">
<input type="reset" value="Reset"/>
<input type="submit" name="save" value="Speichern"/>
</div>
</form>
</py:when>
<py:otherwise>
<h2 id="available-options">Bestellte Artikel</h2>
<table cellspacing="0px" cellpadding="0xp">
<tr>
<th>Artikel</th><th>Beschreibung</th><th>Preis in &euro; inkl. MwSt.</th><th>Anzahl</th>
</tr>
<tr py:for="option in event.options">
<td>${option.name}</td>
<td>${wiki_to_html(context, option.description)}</td>
<td id="price">${"%.2f" % option.price}</td>
<td>${option.count and option.count or 0}</td>
</tr>
<tr><td colspan="3" align="left"><b>Summe:</b></td><td>&euro; ${fee}</td></tr>
</table>
</py:otherwise>
</py:choose>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,52 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<title>Teilnehmer verwalten für ${event.name}</title>
</head>
<body>
<py:choose test="">
<py:when test="event != None">
<h2>Teilnehmer verwalten für ${event.name}</h2>
<form method="post" action="">
<table class="listing">
<thead>
<tr><th>Nick</th><th>Email</th><th>Verwendungszweck 1</th><th>Verwendungszweck 2</th><th>Soll-Betrag</th><th>Eingezahlter Betrag</th><th>Bezahlt</th><th>Registrierungsdatum</th><th>Abgeschlossen</th><th>Löschen</th></tr>
</thead>
<tbody>
<tr py:for="ax, attendee in enumerate(attendees)"
class="${ax % 2 and 'odd' or 'even'}">
<td><a href="${req.href('admin/booking/attendees', event.e_id, attendee.a_id)}">${attendee.nick}</a></td>
<td>${attendee.email}</td>
<td></td>
<td>${("%X" % attendee.a_id).rjust(4, "0")}</td>
<td>${"%s" % attendee.calculate_fee()}</td>
<td><input type="text" name="actual_amount_${attendee.a_id}" value="${attendee.actual_amount and attendee.actual_amount or None}"/></td>
<td><input type="checkbox" name="has_paid" value="${attendee.a_id}" checked="${attendee.has_paid and 'checked' or None}"/></td>
<td>${attendee.time}</td>
<td><input type="checkbox" name="finished" disabled="disabled" value="${attendee.a_id}" checked="${attendee.finished and 'checked' or None}"/></td>
<td><input type="checkbox" name="sel" value="${attendee.a_id}"/></td>
</tr>
</tbody>
</table>
<p><b>Registered Attendees: ${len(attendees)}</b></p>
<div class="buttons">
<input type="submit" name="save" value="Speichern" />
<input type="submit" name="remove_empty" value="Lösche Kunden ohne Bestellungen" />
<input type="submit" name="download_report" value="download Bestellungsreport" />
</div>
</form>
</py:when>
<py:otherwise>
<h2>Bestellung auswählen</h2>
<div><ul><li py:for="e in events"><a href="${req.href('admin/booking/attendees', e.e_id)}">${e.name}</a></li></ul></div>
</py:otherwise>
</py:choose>
<p class="help">
</p>
</body>
</html>

View File

@ -0,0 +1,39 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<title>Bestellungen einsehen für ${eventname}</title>
</head>
<body>
<py:choose test="">
<py:when test="items != None">
<div id='rendezvous-main'>
<table class="listing">
<thead>
<tr><th>Name</th><th>Einzelpreis &euro;</th><th>Anzahl</th><th>Zusammen</th></tr>
</thead>
<tr py:for="ix, item in enumerate(items)" class="${ix%2 and 'even' or 'odd'}">
<td>${item[1]}</td><td>${"%.2f" % float(item[2])}</td><td>${item[3]}</td><td>${"%.2f" % item[4]}</td>
</tr>
</table>
</div>
<p><b>Gesamt Preis: ${sum([item[4] for item in items])}</b></p>
<form method="post" action="">
<div class="buttons">
<input type="submit" name="remove_empty" value="Lösche Kunden ohne Bestellungen" />
<input type="submit" name="download_report" value="download report" />
</div>
</form>
</py:when>
<py:otherwise>
<h2>Bestellung auswählen</h2>
<div><ul><li py:for="e in events"><a href="${req.href('admin/booking/einkauf', e.e_id)}">${e.name}</a></li></ul></div>
</py:otherwise>
</py:choose>
</body>
</html>

View File

@ -0,0 +1,109 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<script type="text/javascript">
/* <![CDATA[ */
jQuery(document).ready(function($) {
$("#gp_date_begin").datepicker({"dateFormat" : "dd.mm.yy"});
$("#gp_date_end").datepicker({"dateFormat" : "dd.mm.yy"});
$("#gp_edit_deadline").datepicker({"dateFormat" : "dd.mm.yy"});
$("#gp_payment_deadline").datepicker({"dateFormat" : "dd.mm.yy"});
});
/* ]]> */
</script>
<title>Events verwalten</title>
</head>
<body>
<h2>Events verwalten</h2>
<form method="post" action="">
<table class="listing">
<thead>
<tr><th>Name</th><th>Von</th><th>Bis</th><th>Löschen</th><th>Duplizieren</th><th>Abschliessen</th></tr>
</thead>
<tbody>
<tr py:for="idx, event in enumerate(events)"
class="${idx % 2 and 'odd' or 'even'}">
<td><a href="${req.href.admin('booking', 'events', event.e_id)}">${event.name}</a></td>
<td><a href="${req.href.admin('booking', 'events', event.e_id)}">${event.time_begin}</a></td>
<td><a href="${req.href.admin('booking', 'events', event.e_id)}">${event.time_end}</a></td>
<td><input type="checkbox" name="rsel" value="${event.e_id}"/></td>
<td><input type="radio" name="copy" value="${event.e_id}"/></td>
<td><input type="radio" name="finish" value="${event.e_id}"/></td>
</tr>
</tbody>
</table>
<div class="buttons">
<input type="submit" name="save" value="Speichern" />
</div>
</form>
<form id="addoption" method="post" action="">
<input py:if="event" type="hidden" name="e_id" value="${event.e_id}"/>
<fieldset>
<fieldset>
<legend>${event and "Event editieren" or "Neues Event"}</legend>
<table>
<tr class="field">
<th><label for="gp_subject">Name:</label></th>
<td ><input id="gp_subject" type="text" name="name" value="${event and event.name or None}"/></td>
</tr>
<tr class="field">
<th><label for="gp_date_begin">Datum Begin:</label></th>
<td><input id="gp_date_begin" type="text" name="date_begin" size="10" maxlength="10" value="${event and event.time_begin.strftime('%d.%m.%Y') or None}"/>
<input id="gp_time_begin" type="text" name="time_begin" size="5" maxlength="5" value="${event and event.time_begin.strftime('%H:%M') or None}"/></td>
</tr>
<tr class="field">
<th><label for="gp_date_end">Datum Ende:</label></th>
<td><input id="gp_date_end" type="text" name="date_end" size="10" maxlength="10" value="${event and event.time_end.strftime('%d.%m.%Y') or None}"/>
<input type="text" name="time_end" size="5" maxlength="5" value="${event and event.time_end.strftime('%H:%M') or None}"/></td>
</tr>
<tr class="field">
<th><label for="gp_edit_deadline">Edit Deadline:</label></th>
<td><input id="gp_edit_deadline" type="text" size="10" maxlength="10" name="edit_deadline_date" value="${event and event.edit_deadline.strftime('%d.%m.%Y') or None}"/>
<input type="text" name="edit_deadline_time" size="5" maxlength="5" value="${event and event.edit_deadline.strftime('%H:%M') or None}"/></td>
</tr>
<tr class="field">
<th><label for="gp_payment_deadline">Bezahl Deadline:</label></th>
<td><input id="gp_payment_deadline" type="text" size="10" maxlength="10" name="payment_deadline_date" value="${event and event.payment_deadline.strftime('%d.%m.%Y') or None}"/>
<input type="text" name="payment_deadline_time" size="5" maxlength="5" value="${event and event.payment_deadline.strftime('%H:%M') or None}"/></td>
</tr>
<tr class="field">
<th><label for="gp_desc">Beschreibung:</label></th>
<td><textarea id="gp_desc" rows="20" cols="80" name="description">${event and event.description or None}</textarea></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>${event_account and "Konto editieren" or "Neues Konto"}</legend>
<table>
<tr class="field">
<th><label for="gp_account_owner">Kontoinhaber:</label></th>
<td><input id="gp_account_owner" type="text" name="account_owner" value="${event_account and event_account.account_owner or 'Chaostreff Dortmund'}"/></td>
</tr>
<tr class="field">
<th><label for="gp_account_no">Kontonummer:</label></th>
<td><input id="gp_account_no" type="text" name="account_no" value="${event_account and event_account.account_no or '4009368600'}"/></td>
</tr>
<tr class="field">
<th><label for="gp_bank_no">Geldinstitut:</label></th>
<td><input id="gp_bank_no" type="text" name="bank_name" value="${event_account and event_account.bank_name or 'GLS-Bank'}"/></td>
</tr>
<tr class="field">
<th><label for="gp_bank_no">BLZ:</label></th>
<td><input id="gp_bank_no" type="text" name="bank_no" value="${event_account and event_account.bank_no or '43060967'}"/></td>
</tr>
<tr class="field">
<th><label for="gp_first_reason">1. Überweisungszweck:</label></th>
<td><input id="gp_first_reason" type="text" name="first_reason" value="${event_account and event_account.first_reason or 'Mate'}"/></td>
</tr>
</table>
</fieldset>
<div class="buttons"><input type="submit" name="add" value="Speichern" /></div>
</fieldset>
</form>
</body>
</html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<title>Optionen verwalten</title>
</head>
<body>
<h2>Optionen verwalten</h2>
<form py:if="options" method="post" action="">
<table class="listing">
<thead>
<tr><th>Name</th><th>Beschreibung</th><th>Preis</th><th>Aktiv</th><th>Min Anzahl</th><th>Max Anzahl</th><th>Löschen</th></tr>
</thead>
<tbody>
<tr py:for="idx, op in enumerate(options)"
class="${idx % 2 and 'odd' or 'even'}">
<td><a href="${req.href.admin('booking', 'options', op.ao_id)}">${op.name}</a></td>
<td>${op.description}</td>
<td>${"%.2f" % op.price}</td>
<td><input type="checkbox" name="actives" value="${op.ao_id}" checked="${op.active==1 and 'checked' or None}"/></td>
<td>${op.min_count}</td>
<td>${op.max_count}</td>
<td><input type="checkbox" name="rsel" value="${op.ao_id}"/></td>
</tr>
</tbody>
</table>
<div class="buttons">
<input type="submit" name="save" value="speichern" />
</div>
</form>
<form id="addoption" method="post" action="">
<fieldset>
<legend>${option.ao_id and "Editiere Option" or "Neue Option"}:</legend>
<table>
<tr class="field">
<th><label for="gp_subject">Name:</label></th>
<td><input id="gp_subject" type="text" size="40" name="name" value="${option.name}"/></td>
</tr>
<tr class="field">
<th><label for="gp_price">Preis:</label></th>
<td><input id="gp_price" type="text" name="price" value="${option.price}"/></td>
</tr>
<tr class="field">
<th><label for="gp_min_count">Min Anzahl:</label></th>
<td><input id="gp_min_count" type="text" name="min_count" value="${option.min_count}"/></td>
</tr>
<tr class="field">
<th><label for="gp_max_count">Max Anzahl:</label></th>
<td><input id="gp_max_count" type="text" name="max_count" value="${option.max_count}"/></td>
</tr>
<tr class="field">
<th><label for="gp_stock_count">Lagerbestand:</label></th>
<td><input id="gp_stock_count" type="text" name="stock_count" value="${option.stock_count}"/></td>
</tr>
<tr class="field">
<th><label for="gp_desc">Beschreibung:</label></th>
<td><textarea id="gp_desc" rows="10" cols="100" name="desc">${option.description}</textarea></td>
</tr>
</table>
<div class="buttons">
<input type="submit" name="add" value="Speichern"/>
</div>
</fieldset>
</form>
</body>
</html>

View File

@ -0,0 +1,69 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<title>$label_plural</title>
</head>
<body>
<h2>Manage RendezVousTypes</h2>
<form id="addsubj" class="addoption" method="post" action="">
<fieldset>
<legend>Verknüpfe Option mit Event:</legend>
<table>
<tr class="field">
<th><label for="event">Option:</label></th>
<td><select id="option" name="option">
<option py:for="option in sorted(options)">${option.name}</option>
</select>
</td>
</tr>
<tr class="field">
<th><label for="event">Event:</label></th>
<td><select id="event" name="event">
<option py:for="event in sorted(events)">${event.name}</option>
</select>
</td>
</tr>
</table>
<p class="help">
Grant permission to a RendezVousType.
</p>
<div class="buttons">
<input type="submit" name="add" value=" Add " />
</div>
</fieldset>
</form>
<form method="post" action="">
<table class="listing" id="permlist">
<thead>
<tr><th>Event</th><th>Default</th><th>Optionen</th></tr>
</thead>
<tbody>
<tr py:for="idx, event in enumerate(events)" py:if="event.options"
class="${idx % 2 and 'odd' or 'even'}">
<td>${event.name}</td>
<td><input type="radio" name="default" value="${event.e_id}" checked=""/></td>
<td>
<div py:for="option in event.options">
<input type="checkbox" id="${event.e_id}:${option.ao_id}" name="sel" value="${event.e_id}:${option.ao_id}"/>
<label for="${event.e_id}:${option.ao_id}">${option.name}</label>
</div>
</td>
</tr>
</tbody>
</table>
<div class="buttons">
<input type="submit" name="save" value="Save Changes" />
</div>
</form>
<p class="help">
</p>
</body>
</html>

View File

@ -0,0 +1,63 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<title>Benachrichtigungen verwalten</title>
</head>
<body>
<py:choose test="">
<py:when test="reminders != None">
<h2>Benachrichtigungen verwalten für $eventname</h2>
<div class="admin-block">
<form method="post" action="">
<table class="listing">
<thead>
<tr><th>ID</th><th>Text</th><th>Versendedatum</th><th>Wurde gesendet</th><th>Löschen</th></tr>
</thead>
<tbody>
<tr py:for="ax, reminder in enumerate(reminders)" class="${ax % 2 and 'odd' or 'even'}">
<td><a href="${req.href.admin('reminder', 'edit', reminder.reminder_id)}">${reminder.reminder_id}</a></td>
<td>${wiki_to_html(context, reminder.text &lt; 30 and reminder.text or reminder.text[:30] + "...")}</td>
<td>${reminder.notify_on}</td>
<td>${reminder.was_send_on}</td>
<td><input type="checkbox" name="sel" value="${reminder.a_id}"/></td>
</tr>
</tbody>
</table>
<p><b>Registrierte Benachrichtigungen: ${len(reminders)}</b></p>
<div class="buttons">
<input type="submit" name="save" value="speichern" />
</div>
</form>
</div>
<div>
<form id="addoption" method="post" action="">
<fieldset>
<legend>Neuer Reminder:</legend>
<table>
<tr class="field">
<th><label for="gp_notify">Datum und Uhrzeit:</label></th>
<td><input id="gp_notify" type="text" name="notify_on" /></td>
</tr>
<tr class="field">
<th><label for="gp_text">Nachricht:</label></th>
<td><textarea id="gp_text" rows="20" cols="100" name="text" /></td>
</tr>
</table>
<p class="help">Erstelle neuen Reminder.</p>
<div class="buttons"><input type="submit" name="add" value="Speichern" /></div>
</fieldset>
</form>
</div>
</py:when>
<py:otherwise>
<h2>Event wählen</h2>
<div><ul><li py:for="e in events"><a href="${req.href('admin/booking/reminder', e.e_id)}">${e.name}</a></li></ul></div>
</py:otherwise>
</py:choose>
</body>
</html>

View File

@ -0,0 +1,20 @@
<!DOCTYPE htm
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<title>Bitte bestätigen</title>
</head>
<body>
<div id="content">
<h2>$query</h2>
<form method="post">
<input type="submit" name="$query_false" value="Abbrechen"/>
<input type="submit" name="$query_true" value="Fortfahren"/>
</form>
</div>
</body>
</html>

View File

@ -0,0 +1,26 @@
<!DOCTYPE htm
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<script type="text/javascript">
/* <![CDATA[ */
jQuery(document).ready(function($) {$("#nr_nick").get(0).focus()});
/* ]]> */
</script>
<title>Übersicht Sammelbestellungen</title>
</head>
<body>
<div id="content">
<h1>Sammelbestellungen</h1>
<div id='rendezvous-main'>
<ul>
<li py:for="event in events"><a href="${req.href.booking(event.e_id)}">${event.name}</a></li>
</ul>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE htm
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<script type="text/javascript">
/* <![CDATA[ */
jQuery(document).ready(function($) {$("#nr_nick").get(0).focus()});
/* ]]> */
</script>
<title>Anmeldung f&uuml;r ${event.name}</title>
</head>
<body>
<div id="content">
<h1>Teilnahme an '${event.name}'</h1>
<!--<table>
<tr>
<th><label for=""></label></th><td id=""></td>
</tr>
<tr>
<th><label for=""></label></th><td id=""></td>
</tr>
<tr>
<th><label for=""></label></th><td id=""></td>
</tr>
</table>-->
<div>${wiki_to_html(context, event.description, escape_newlines=preserve_newlines)}</div>
<div py:if="event.edit_deadline &gt; now">Editierbar bis ${event.edit_deadline.strftime('%d.%m.%Y %H:%M')} Uhr.</div>
<div>Der Betrag deiner Bestellung muss spätestens am ${event.payment_deadline.strftime('%d.%m.%Y %H:%M')} Uhr auf unserem Konto eingegangen sein!!!</div>
<div id='booking-attendee'>
<form name="register" uri="" method="post" mime-type="text/plain" action="">
<fieldset>
<legend>Wichtige Daten</legend>
<table class="register">
<tr><th><label for="nr_nick">Nick</label></th><td>${authname}</td></tr>
<tr><th><label for="nr_email">Email (optional):</label></th><td><input id="nr_email" type="text" size="30" maxlength="100" name="email" /></td></tr>
</table>
</fieldset>
<div class="mybuttons">
<input type="reset" value="Reset"/>
<input type="submit" name="register" value="Ich will mitbestellen!!!!"/>
</div>
</form>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,162 @@
<!DOCTYPE htm
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<script type="text/javascript">
$(document).ready(function() {
$('input[name^="count"], input[name="email"]').change(function() {
$(this).closest("form").submit();
});
});
</script>
<title>Deine Artikel f&uuml;r ${event.name}</title>
</head>
<body>
<div id="content" py:with="fee = attendee.calculate_fee()">
<div id="event_details" class="booking-block">
<h1>${event.name}</h1>
<div>${wiki_to_html(context, event.description, escape_newlines=preserve_newlines)}</div>
<table class="properties" cellspacing="10px">
<!--<py:if test="event.edit_deadline &lt; now or register_deadline &lt; now"> -->
<!--<tr>
<th id="timeframe">Bestellung Zeitraum:</th>
<td headers="timeframe">${event.time_begin.strftime('%d.%m.%Y %H:%M')} - ${event.time_end.strftime('%d.%m.%Y %H:%M')}</td>
</tr>-->
<!--<tr>
<th id="register_deadline" py:if="event.register_deadline &gt; now">Registrierungs-Zeitraum:</th>
<td headers="register_deadline">${event.register_deadline.strftime('%d.%m.%Y %H:%M')} Uhr.</td>
</tr>-->
<tr>
<th id="edit-deadline">Bestellungen können verändert werden bis zum:</th>
<td headers="edit-deadline" id="edit-deadline-data">${event.edit_deadline.strftime('%d.%m.%Y %H:%M')} Uhr.</td>
</tr>
<!--</py:if> -->
<tr>
<th id="payment-deadline">Geld bitte auf unten angebenes Konto einzahlen bis zum:</th>
<td headers="payment-deadline" id="payment-deadline-data">${event.payment_deadline.strftime('%d.%m.%Y %H:%M')} Uhr.</td>
</tr>
<tr>
<td colspan="2">Der Liefertermin wird per Mail bekannt gegeben, sofern Du eine Mailaddresse angegeben hast.</td>
</tr>
<tr>
<td colspan="2">Du kannst Deine Registrierung unten komplett löschen.</td>
</tr>
<tr>
<td colspan="2">Wenn Du javascript angeschaltet hast, werden Änderungen beim Verlassen des Eingabefeldes automatisch gespeichert.
Wenn Du javascript blockiert hast, musst Du nach Änderungen manuell auf den Speichern Button klicken.</td>
</tr>
<tr>
<td colspan="2">Wenn Du mit Deiner Bestellung zufrieden bist, und die Bestellung abschliessen möchtest, dann drücke bitte
auf Abschliessen. Dann findest Du unter "Deine Daten" einen Link, mit dem Du eine Zusammenfassung Deiner Bestellung und alle relevanten
Daten zum bequemen Bezahlen Deines Anteiles als pdf herunterladen kannst.</td>
</tr>
</table>
</div>
<div id="booking-attendee" class="booking-block">
<h1 id="anmeldungsdaten">Deine Daten</h1>
<form name="options" uri="#anmeldungsdaten" method="post" mime-type="text/plain" action="">
<table class="properties" cellspacing="10px">
<tr>
<th id="h_author">Nick</th>
<td headers="h_author">${attendee.nick}</td>
</tr>
<tr>
<th id="h_email">Email</th>
<td headers="h_email"><input name="email" size="50" value="${attendee.email}"/></td>
</tr>
<tr>
<th id="h_paid">Bezahlt</th>
<td headers="h_paid">${bool(attendee.has_paid) and "Ja" or "Nein"}</td>
</tr>
</table>
<div class="mybuttons">
<input type="reset" value="Reset"/>
<input py:if="attendee.finished" type="submit" name="download_invoice" value="Abrechnung herunterladen"/>
<input py:if="not attendee.finished" type="submit" name="finish" value="Bestellung abschliessen"/>
<input py:if="not attendee.finished" type="submit" name="unregister" value="Komplette Bestellung löschen"/>
<input type="submit" name="attendee-save" value="Speichern"/>
</div>
</form>
</div>
<div py:if="attendee.finished" id="booking-bank" class="booking-block">
<h1 id="kontodaten">Bezahlvorgang per Überweisung</h1>
<table class="properties" cellspacing="10px">
<tr>
<th id="name">Kontoinhaber</th>
<td headers="name">${account_data.account_owner}</td>
</tr>
<tr>
<th id="konto">Konto</th>
<td headers="konto">${account_data.account_no}</td>
</tr>
<tr>
<th id="blz">Blz</th>
<td headers="blz">${account_data.bank_no}</td>
</tr>
<tr>
<th id="betrag">Betrag</th>
<td headers="betrag">&euro; ${fee}</td>
</tr>
<tr>
<th id="bank">Bank</th>
<td headers="bank">${account_data.bank_name}</td>
</tr>
<tr>
<th id="h_extid">1. Überweisungszweck</th>
<td headers="h_extid">${account_data.first_reason}</td>
</tr>
<tr>
<th id="h_extid">2. Überweisungszweck</th>
<td headers="h_extid">${("%X" % attendee.a_id).rjust(4, "0")}</td>
</tr>
</table>
</div>
<div id='booking-options' class="booking-block">
<py:choose test="">
<py:when test="not attendee.finished">
<h1 id="available-options">Verfügbare Artikel</h1>
<form id="options" name="options" method="post" mime-type="text/plain" action="">
<table cellspacing="0px" cellpadding="0xp">
<tr>
<th>Artikel</th><th>Beschreibung</th><th>Preis in &euro; inkl. MwSt.</th><th>Anzahl</th>
</tr>
<tr py:for="option in event.options">
<td>${option.name}</td>
<td>${wiki_to_html(context, option.description)}</td>
<td id="price">${"%.2f" % option.price}</td>
<td>
<input id="count_${option.ao_id}" type="text" size="5" maxlength="4" name="count_${option.ao_id}" value="${option.count and option.count or 0}"/>
</td>
</tr>
<tr><td colspan="3" align="left"><b>Summe:</b></td><td>&euro; ${fee}</td></tr>
</table>
<div class="mybuttons">
<input type="reset" value="Reset"/>
<input type="submit" name="save" value="Speichern"/>
</div>
</form>
</py:when>
<py:otherwise>
<h1 id="available-options">Bestellte Artikel</h1>
<table cellspacing="0px" cellpadding="0xp">
<tr>
<th>Artikel</th><th>Beschreibung</th><th>Preis in &euro; inkl. MwSt.</th><th>Anzahl</th>
</tr>
<tr py:for="option in event.options">
<td>${option.name}</td>
<td>${wiki_to_html(context, option.description)}</td>
<td id="price">${"%.2f" % option.price}</td>
<td>${option.count and option.count or 0}</td>
</tr>
<tr><td colspan="3" align="left"><b>Summe:</b></td><td>&euro; ${fee}</td></tr>
</table>
</py:otherwise>
</py:choose>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE htm
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="layout.html" />
<head>
<title>Registrierung gelöscht!</title>
</head>
<body>
<div id="content">
<h2>Deine Registrierung und alle dazugehörigen Daten sind erfolgreich gelöscht worden!</h2>
<h3><a href="${req.href.wiki()}">Zurück zum wiki</a></h3>
</div>
</body>
</html>

View File

@ -0,0 +1,62 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/">
<xi:include href="admin.html" />
<head>
<title>Accounts: Configuration</title>
</head>
<body>
<h2>Manage Site Files</h2>
<py:if test="'USER_UPLOAD' in perm(context.resource)">
<form id="addfile" action="" class="addnew" method="post" enctype="multipart/form-data">
<fieldset>
<legend>Upload File:</legend>
<div class="field">
<label>File: <input type="file" name="site_file"
disabled="${readonly and 'disabled' or None}" /></label>
</div>
<p class="help" py:choose="True">
<py:when test="readonly">
The web server does not have sufficient permissions to
store files in your home directory.
</py:when>
<py:otherwise>
Upload a file to your home directory.
</py:otherwise>
</p>
<div class="buttons">
<input type="submit" name="upload" value="Upload"
disabled="${readonly and 'disabled' or None}" />
</div>
</fieldset>
</form>
</py:if>
<form method="post">
<table class="listing" id="sitelist">
<thead>
<tr><th colspan="3" style="text-align:center;">Content of ${project.name}/users/${authname}</th></tr>
<tr><th class="sel">delete</th><th>Filename</th><th>Size</th></tr>
</thead>
<tbody>
<tr py:for="file in files" >
<td><input type="checkbox" name="sel" value="${file.name}"/></td>
<td>${file.link}</td>
<td>${file.size}</td>
</tr>
</tbody>
</table>
<py:if test="'USER_UPLOAD_MANAGE' in perm(context.resource)">
<div class="buttons">
<input type="submit" name="delete" value="Delete selected files"
disabled="${readonly and 'disabled' or None}" />
</div>
</py:if>
</form>
</body>
</html>

View File

@ -0,0 +1,103 @@
# -*- coding: utf-8 -*-
from sys import maxint
from datetime import datetime, date, time
from trac.util.datefmt import utc, to_timestamp, localtz, format_time, get_timezone, timezone
from re import match, compile as re_compile
class ValidationError(ValueError):
def __str__(self):
return "ValidationError: value out of bounds!"
def validate_id(value):
if (0 > value > maxint):
raise ValidationError("invalid argument")
def make_hash():
from random import sample
population = '123456789ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
return "".join(sample(population, 4))
def date_parse(foo):
groups = match("^(\d{1,2}).(\d{1,2}).(\d{4})$", foo).groups()
return date(int(groups[2]),int(groups[1]),int(groups[0]))
def time_parse(foo):
timeM = re_compile("^(\d{1,2}):?(\d{2})$")
res = timeM.match(foo)
if not res:
raise ValueError("not valid time format")
h,m = res.groups()
return time(int(h),int(m))
def validate_email(addr):
rfc822_specials = '()<>@,;:\\"[]'
# First we validate the name portion (name@domain)
c = 0
while c < len(addr):
if addr[c] == '"' and (not c or addr[c - 1] == '.' or addr[c - 1] == '"'):
c = c + 1
while c < len(addr):
if addr[c] == '"':
break
if addr[c] == '\\' and addr[c + 1] == ' ':
c = c + 2
continue
if ord(addr[c]) < 32 or ord(addr[c]) >= 127:
return 0
c = c + 1
else:
return False
if addr[c] == '@':
break
if addr[c] != '.':
return False
c = c + 1
continue
if addr[c] == '@':
break
if ord(addr[c]) <= 32 or ord(addr[c]) >= 127:
return False
if addr[c] in rfc822_specials:
return False
c = c + 1
if not c or addr[c - 1] == '.':
return False
# Next we validate the domain portion (name@domain)
domain = c = c + 1
if domain >= len(addr):
return False
count = 0
while c < len(addr):
if addr[c] == '.':
if c == domain or addr[c - 1] == '.':
return False
count = count + 1
if ord(addr[c]) <= 32 or ord(addr[c]) >= 127:
return False
if addr[c] in rfc822_specials:
return False
c = c + 1
return count >= 1
def get_tz(session_tzname):
if session_tzname == 'UTC':
selected_tz = utc
else:
try:
selected_tz = timezone(session_tzname)
except Exception, e:
print e
selected_tz = utc
return session_tzname, selected_tz
def get_option_count(a, aopt):
aopt.count = 0
print a, a.options
for i in a.options:
if i.ao_id == aopt.ao_id:
i.name = aopt.name
aopt.count = i.count
return

View File

@ -0,0 +1,507 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from genshi.builder import tag
from model import *
from os import mkdir
import os.path
from pkg_resources import resource_filename
from re import match, sub
from trac.admin import IAdminPanelProvider
from trac.config import *
from trac.core import Component, implements, TracError
import shutil
import stat
import time
from threading import Thread, Lock
from trac.perm import PermissionError, IPermissionRequestor
from trac.util.datefmt import utc
from trac.util.html import html
from trac.util import Markup, pretty_size
from trac.util.translation import _
from trac.util.datefmt import utc, to_timestamp
from trac.web import RequestDone
from trac.web.chrome import INavigationContributor, ITemplateProvider, add_stylesheet, add_script, add_warning, add_ctxtnav, add_notice
from trac.web import IRequestHandler
from tracbooking.report import create_attendee_report
from tracbooking.utils import validate_id, make_hash, get_option_count, get_tz, validate_email
__all__ = ['BookingComponent', 'UserUploadComponent']
class BookingComponent(Component):
'''The web ui frontend or the rendezvous system'''
implements(INavigationContributor,
IRequestHandler,
IPermissionRequestor,
ITemplateProvider)
Option("booking", "account_owner", u"Chaostreff Dortmund")
Option("booking", "account", u"4009368600 ")
Option("booking", "bank_no", u"43060967 ")
Option("booking", "bank_name", u"GLS-Bank")
Option("booking", "first_reason", u"BBQ2010")
# IPermissionRequestor methods
def get_permission_actions(self):
'''returns all permissions this component provides'''
return ["BOOKING_VIEW", ("BOOKING_ADMIN", ("BOOKING_VIEW"))]
# INavigationContributor methods
def get_active_navigation_item(self, req):
return 'booking'
def get_navigation_items(self, req):
if not "BOOKING_VIEW" in req.perm:
return
yield ('mainnav', 'booking', html.A('Sammelbestellungen', href= req.href.booking()))
def match_request(self, req):
key = req.path_info
if key == '/booking':
return True
m = match(r'/booking/(\d+)$', key)
if m:
req.args['event_id'] = int(m.group(1))
return True
return False
def process_request(self, req):
req.perm.require("BOOKING_VIEW")
query = req.path_info
add_stylesheet (req, 'hw/css/booking.css')
if not req.args.has_key("event_id"):
return self._display_overview(req)
e_id = req.args["event_id"]
a = Attendee.fetch_one(self.env, nick=req.authname, e_id=e_id)
if a:
return self._process_status(req, a)
else:
if self.env.config.get("booking", "autoregister"):
event = Event.fetch_one(self.env, e_id)
nick = req.authname
rd = Attendee(self.env, 0, event.e_id, 0, nick, None, 0, 0, datetime.now(utc), 0)
rd.commit()
return self._process_status(req, rd)
return self._process_register(req)
# ITemplateProvider methods
# Used to add the plugin's templates and htdocs
def get_templates_dirs(self):
return [resource_filename(__name__, 'templates'),]
def get_htdocs_dirs(self):
"""Return a list of directories with static resources (such as style
sheets, images, etc.)
Each item in the list must be a `(prefix, abspath)` tuple. The
`prefix` part defines the path in the URL that requests to these
resources are prefixed with.
The `abspath` is the absolute path to the directory containing the
resources on the local file system.
"""
return [('hw', resource_filename(__name__, 'htdocs'))]
def _process_register(self, req):
'''process add,change,delete actions for dates'''
data = {"now" : datetime.now(utc)}
#a = Attendee.fetch_one(self.env, nick=req.authname)
#if a:
#req.redirect(req.href.booking("status"))
e_id = req.args["event_id"]
event = Event.fetch_one(self.env, e_id)
if not event:
raise TracError("Event konnte nicht gefunden werden")
data["event"] = event
if req.method == "POST":
if req.args.has_key("register"):
nick = req.authname
if req.args.has_key("email"):
email = req.args.get("email")
if email and not validate_email(email):
add_warning(req, u"email ungültig")
return "booking_register.html", data, None
rd = Attendee(self.env, 0, event.e_id, 0, nick, email, 0, 0, datetime.now(utc), 0)
rd.commit()
req.redirect(req.href.booking(e_id))
return 'booking_register.html', data, None
def _display_overview(self, req):
return "booking_events.html", {"events": Event.fetch_all(self.env)}, None
def _process_status(self, req, attendee):
'''display the status if already registered and optional features'''
add_stylesheet (req, 'hw/css/booking.css')
notice = "Bestellung erfolgreich gespeichert."
if req.session.has_key("notice"):
add_notice(req, req.session["notice"])
del req.session["notice"]
req.session.save()
#attendee.finished = False
#attendee.update()
if req.method == "POST":
if not attendee.finished:
failure = False
if req.args.has_key("unregister"):
return "booking_accept.html", {"query": u"Möchtest Du wirklich Deine Bestellung komplett löschen?", "query_true" : "unregister_true", "query_false" : "unregister_false"}, None
elif req.args.has_key("unregister_true"):
UserUploadComponent(self.env).clean_userdir(attendee)
Attendee.delete(self.env, attendee.a_id)
req.redirect(req.href.wiki())
elif req.args.has_key("unregister_false") or req.args.has_key("finish_false"):
pass
elif req.args.has_key("finish"):
return "booking_accept.html", {"query": u"Möchtest Du wirklich Deine Bestellung abschliessen?", "query_true" : "finish_true", "query_false" : "finish_false"}, None
elif req.args.has_key("finish_true"):
attendee.finished = True
attendee.update()
req.redirect(req.href.booking(req.args["event_id"]))
elif req.args.has_key("attendee-save"):
email = req.args["email"]
if not validate_email(email):
add_warning(req, u"email nicht gültig")
attendee.email = email
attendee.update()
req.session["notice"] = "Daten erfolgreich aktualisiert."
else:
args = req.args
for arg in args:
if arg.startswith("count"):
try:
prefix, ao_id = arg.split("_", 1)
ao_id = int(ao_id)
count = int(args[arg])
validate_id(count)
validate_id(ao_id)
except ValueError:
add_warning(req, u"Bitte für Anzahlfelder nur positive Zahen eingeben.")
failure = True
continue
aoption = AvailableOption.fetch_one(self.env, ao_id, fetch_variations=False)
if not aoption:
add_warning(req, u"Artikel %r nicht gefunden" % ao_id)
failure = True
continue
elif not aoption.active:
add_warning(req, u"Artikel %r nicht aktiviert" % ao_id)
failure = True
continue
if count < aoption.min_count:
add_warning(req, u"Artikel '%s' kann minimal '%d' Mal bestellt werden;-)" % (aoption.name, aoption.min_count))
failure = True
continue
elif aoption.max_count and count > aoption.max_count:
add_warning(req, u"Artikel '%s' kann maximal '%d' Mal bestellt werden;-)" % (aoption.name, aoption.max_count))
failure = True
continue
if not count:
BookingOption.delete(self.env, attendee.a_id, ao_id)
else:
opt = BookingOption.fetch_one(self.env, attendee.a_id, ao_id)
if not opt:
opt = BookingOption(self.env, 0, attendee.a_id, ao_id, count)
opt.commit()
else:
opt.count = count
opt.update()
#elif arg.startswith("var"):
#prefix, variation_id = arg.split("_", 1)
#try:
#variation_id = int(variation_id)
#validate_id(variation_id)
#value = int(args[arg])
#validate_id(value)
#except (ValueError,):
#add_warning(req, u"Bitte eine Zahl eingeben;-)")
#failure = True
#continue
#variation = BookingOptionVariation.fetch_one(self.env, attendee.a_id, variation_id)
#if not variation:
#b = BookingOptionVariation(self.env, attendee.a_id, variation_id, value)
#b.commit()
#else:
#BookingOptionVariation.update(self.env, attendee.a_id, variation_id, value)
print "before redirect"
if failure:
req.redirect(req.href.booking(req.args["event_id"]))
else:
req.session["notice"] = notice
req.redirect(req.href.booking(req.args["event_id"]))
elif req.args.has_key("download_invoice"):
e_id = req.args["event_id"]
event = Event.fetch_one(self.env, e_id)
session_tzname, selected_tz = get_tz(req.session.get('tz', self.env.config.get("trac", "default_timezone") or None))
data = create_attendee_report(self.env, event, attendee, selected_tz)
data_len = len(data)
req.send_response(200)
req.send_header("Content-Type", "text/pdf;charset=utf-8")
req.send_header("Content-Length", data_len)
req.send_header("Content-Disposition", 'filename=%s.pdf' % event.name.replace("/", "_").replace(u" ", u"_"))
req.end_headers()
req.write(data)
raise RequestDone
else:
raise Exception("unhandled state")
else:
attendee = Attendee.fetch_one(self.env, nick=req.authname, e_id=req.args["event_id"], fetch_options=True)
event = Event.fetch_one(self.env, e_id=req.args["event_id"], fetch_options=True, only_active=True, attendee_id=attendee.a_id)
if event.options:
for i in event.options:
get_option_count(attendee, i)
data = {"event" : event,
"attendee" : attendee,
"now" : datetime.now(utc),
"account_data" : EventAccount.fetch_by_event(self.env, event.e_id)}
return 'booking_status.html', data, None
class UserUploadComponent(Component):
implements(IRequestHandler, IPermissionRequestor)
def match_request(self, req):
key = req.path_info
m = match(r'/upload/(\d+)$', key)
if m:
req.args['event_id'] = int(m.group(1))
return True
m = match(r'/showupload/(\d+)$', key)
if m:
req.args['attendee_id'] = int(m.group(1))
return True
return False
def process_request(self, req):
req.perm.require('USER_UPLOAD')
a = Attendee.fetch_one(self.env, e_id=req.args["attendee_id"], nick=req.authname)
if not a:
req.redirect(req, req.href.booking(req.args["event_id"]))
target_path = os.path.join(self.env.path, 'htdocs', 'attendees', a.ext_id)
readonly = False
if not os.path.exists(os.path.join(self.env.path, 'htdocs', 'attendees')):
os.mkdir(os.path.join(self.env.path, 'htdocs', 'attendees'))
if not os.path.exists(target_path):
os.mkdir(target_path)
if not (os.path.isdir(target_path) and os.access(target_path, os.F_OK + os.W_OK)):
readonly = True
if req.method == 'POST':
if req.args.has_key('delete'):
self._do_delete(req, target_path)
elif req.args.has_key('upload'):
self._do_upload(req, target_path)
else:
self.log.warning('Unknown POST request: %s', req.args)
req.redirect(self.env.href.upload(req.args["event_id"]))
data = {'readonly' : readonly}
self._render_view(req, data, target_path)
return 'userupload.html', data, None
def clean_userdir(self, attendee):
if not attendee:
return
target_path = os.path.join(self.env.path, 'htdocs', 'attendees', attendee.ext_id)
shutil.rmtree(target_path, True)
# IPermissionRequestor
def get_permission_actions(self):
return ['USER_UPLOAD',]
def _render_view(self, req, data, target_path):
"""Display list of files in trac env htdocs dir"""
filelist = []
if os.path.exists(target_path) and os.path.isdir(target_path):
dlist = os.listdir(target_path)
for f in dlist:
fsize = os.stat(os.path.join(target_path, f))[stat.ST_SIZE]
filelist.append({'name' : f,
'link' : Markup('<a href="%s">%s</a>') % (self.env.href.showupload(req.authname), f),
'size' : pretty_size(fsize)})
continue
data.update({'files' : filelist})
return
def _do_delete(self, req, target_path):
"""Delete a file from htdocs"""
err_list = []
sel = req.args.get('sel')
sel = isinstance(sel, list) and sel or [sel]
for key in sel:
try:
os.unlink(os.path.join(target_path, key))
except OSError:
err_list.append(key)
continue
if err_list:
errmsg = "Unable to delete the following files:\n"
errmsg += '\n'.join(err_list)
raise TracError, errmsg
def _do_upload(self, req, target_path):
"""Install a plugin."""
if not req.args.has_key('site_file'):
raise TracError('No file uploaded')
upload = req.args['site_file']
if not upload.filename:
raise TracError('No file uploaded')
upload_filename = upload.filename.replace('\\', '').replace(':', '').replace('/', '')
upload_filename = os.path.basename(upload_filename)
if not upload_filename:
raise TracError('No file uploaded')
a = Attendee.fetch_one(self.env, nick=req.authname, e_id=req.args["event_id"])
if not a:
req.redirect(req, req.href.booking(req.args["event_id"]))
file_path = os.path.join(target_path, upload_filename)
if os.path.exists(file_path):
raise TracError('A file/directory >>%s<< already exists' % upload_filename)
self.log.info('Installing plugin %s', upload_filename)
flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL
try:
flags += os.O_BINARY
except AttributeError:
# OS_BINARY not available on every platform
pass
target_file = os.fdopen(os.open(file_path, flags), 'w')
try:
shutil.copyfileobj(upload.file, target_file)
self.log.info('File %s uploaded to %s', upload_filename,
target_path)
finally:
target_file.close()
class UploadComponent(Component):
implements(IRequestHandler, IPermissionRequestor)
def match_request(self, req):
key = req.path_info
if key in ("/upload", "/showupload"):
return True
return False
def process_request(self, req):
req.perm.require('USER_UPLOAD')
target_path = os.path.join(self.env.path, 'htdocs', "data")
readonly = False
if not os.path.exists(target_path):
os.mkdir(target_path)
if not (os.path.isdir(target_path) and os.access(target_path, os.F_OK + os.W_OK)):
readonly = True
if req.method == 'POST':
if req.args.has_key('delete'):
self._do_delete(req, target_path)
elif req.args.has_key('upload'):
self._do_upload(req, target_path)
else:
self.log.warning('Unknown POST request: %s', req.args)
req.redirect(self.env.href.upload())
data = {'readonly' : readonly}
self._render_view(req, data, target_path)
return 'userupload.html', data, None
def clean_userdir(self, attendee):
if not attendee:
return
target_path = os.path.join(self.env.path, 'htdocs', 'attendees', attendee.ext_id)
shutil.rmtree(target_path, True)
# IPermissionRequestor
def get_permission_actions(self):
return ['USER_UPLOAD',]
def _render_view(self, req, data, target_path):
"""Display list of files in trac env htdocs dir"""
filelist = []
if os.path.exists(target_path) and os.path.isdir(target_path):
dlist = os.listdir(target_path)
for f in dlist:
fsize = os.stat(os.path.join(target_path, f))[stat.ST_SIZE]
filelist.append({'name' : f,
'link' : Markup('<a href="%s">%s</a>') % (self.env.href.showupload(req.authname), f),
'size' : pretty_size(fsize)})
continue
data.update({'files' : filelist})
return
def _do_delete(self, req, target_path):
"""Delete a file from htdocs"""
err_list = []
sel = req.args.get('sel')
sel = isinstance(sel, list) and sel or [sel]
for key in sel:
try:
os.unlink(os.path.join(target_path, key))
except OSError:
err_list.append(key)
continue
if err_list:
errmsg = "Unable to delete the following files:\n"
errmsg += '\n'.join(err_list)
raise TracError, errmsg
def _do_upload(self, req, target_path):
"""Install a plugin."""
if not req.args.has_key('site_file'):
raise TracError('No file uploaded')
upload = req.args['site_file']
if not upload.filename:
raise TracError('No file uploaded')
upload_filename = upload.filename.replace('\\', '').replace(':', '').replace('/', '')
upload_filename = os.path.basename(upload_filename)
if not upload_filename:
raise TracError('No file uploaded')
a = Attendee.fetch_one(self.env, nick=req.authname, e_id=req.args["event_id"])
if not a:
req.redirect(req, req.href.booking(req.args["event_id"]))
file_path = os.path.join(target_path, upload_filename)
if os.path.exists(file_path):
raise TracError('A file/directory >>%s<< already exists' % upload_filename)
self.log.info('Installing plugin %s', upload_filename)
flags = os.O_CREAT + os.O_WRONLY + os.O_EXCL
try:
flags += os.O_BINARY
except AttributeError:
# OS_BINARY not available on every platform
pass
target_file = os.fdopen(os.open(file_path, flags), 'w')
try:
shutil.copyfileobj(upload.file, target_file)
self.log.info('File %s uploaded to %s', upload_filename,
target_path)
finally:
target_file.close()
#class TracSchedulerTest(Component):
#implements( IScheduledTask)
#def process_scheduled_task(self, parent):
#sqlString = "SELECT edit_deadline FROM booking_event;"
#rows = parent.queryDb(sqlString)
#n = datetime.now(utc)
#for i in rows:
#d = datetime.fromtimestamp(i[0], utc)
#dt = d - n
#if dt < timedelta(0,3600):
#parent.queryDb("UPDATE booking_available_option SET active=0 where ao_id in (1,2,3,7);", commit=True)
#parent.queryDb("UPDATE booking_available_option SET active=1 where ao_id in (4,5,6,8);", commit=True)