- see COPYING.AGPL for details
-->
<Wolf>
- <Table name="cart">
+ <Table name="cart" backup="no">
<Column name="cartid" type="string:32" primarykey="yes">
the cookie for this cart
<Call lang="php" method="WebCart::getNewCartId()"/>
pointer to shipping type (none per default, programmatic default is in config)
</Column>
</Table>
- <Table name="cartticket">
+ <Table name="cartticket" backup="no">
<Column name="cartid" type="string:32" notnull="yes" foreignkey="cart:cartid" primarykey="yes"/>
<!--tickets in the cart-->
<Column name="eventid" type="int32" notnull="yes" foreignkey="event:eventid" primarykey="yes"/>
<Column name="amount" type="int32" notnull="yes"/>
<!-- Column name="seating" type="string:32" null="yes"/ -->
</Table>
- <Table name="cartvoucher">
+ <Table name="cartvoucher" backup="no">
<Column name="cvid" type="seq64" primarykey="yes"/>
<Column name="cartid" type="string:32" notnull="yes" foreignkey="cart:cartid"/>
<Column name="value" type="int32" notnull="yes">voucher value in cents</Column>
</Table>
- <Table name="cartitem">
+ <Table name="cartitem" backup="no">
<Column name="ciid" type="seq64" primarykey="yes"/>
<Column name="cartid" type="string:32" notnull="yes" foreignkey="cart:cartid"/>
<Column name="productid" type="int32" foreignkey="product:productid" notnull="yes"/>
<Column name="amount" type="int32" notnull="yes"/>
</Table>
- <Table name="websession">
+ <Table name="websession" backup="no">
<Column name="sessionid" type="string:64" primarykey="yes"/>
<!--/customer-->
<Column name="customerid" type="int32" notnull="yes" foreignkey="customer:customerid"/>
<Abstract lang="php"/>
<Doc>The cart as used by the web user interface, this maps into the cart tables. This class is never used by the </Doc>
<Property name="cartid" type="astring">The cart ID of this session</Property>
+ <Property name="timeout" type="int">When the cart will be automatically deleted</Property>
<Property name="customerid" type="int32">The customer of this cart</Property>
<Property name="deliveryaddressid" type="int64">The address to deliver to (mandatory)</Property>
<Property name="invoiceaddressid" type="int64">The address to send the invoice to if different from the delivery address (optional)</Property>
<Mapping table="cart">
<Map property="cartid"/>
+ <Map property="timeout"/>
<Map property="customerid"/>
<Map property="deliveryaddressid" column="deliveryaddress"/>
<Map property="invoiceaddressid" column="invoiceaddress"/>
<Column name="host" type="string:64" notnull="yes" foreignkey="host:hostname" primarykey="yes"/>
</Table>
- <Table name="session">
+ <Table name="session" backup="no">
<Column name="sessionid" type="string:64" primarykey="yes"/>
<Column name="uname" type="string:64" notnull="yes" foreignkey="user:uname"/>
<!-- unix timestamp at which to delete this session -->
$d=dirname(__FILE__);
wob_autoclass("EventRender",$d.'/event_listing.php');
wob_autoclass("WebCart",$d.'/cart_listing.php');
+
wob_autoclass("LangFilterExtension",$d.'/twig_extensions.php');
+wob_autoclass("SmokeFilterExtension",$d.'/twig_extensions.php');
?>
//get the cart
$cartid=self::getOrCreateCart();
//find event
- if(!isset($HTTPARGS['event']) || !isset($HTTPARGS[self::TicketAmountField]))
+ if(!isset($HTTPARGS[self::TicketAmountField]))
redirectHome();
- $evid=$HTTPARGS['event']+0;
//find price categories
$pcs=$HTTPARGS[self::TicketAmountField];
if(!is_array($pcs) || count($pcs)==0)
redirectHome();
//go through them
foreach($pcs as $pcid => $amount){
- $pcid=$pcid+0;$amount=$amount+0;
+ //extract event ID from pcid
+ $evid=WOEventPrice::getEventIdFromAmountIndex($pcid);
+ //convert pcid to int
+ $pcid=WOEventPrice::getCategoryIdFromAmountIndex($pcid);
+ //convert amount to int
+ $amount=$amount+0;
+ //actually add
WTrWebCartAddTicket::execute($cartid,$evid,$pcid,$amount);
}
//go to the cart
/** creates the cart overview */
static public function createCartOverview()
{
- global $twig,$basevars;
+ global $twig,$basevars,$WebForceShipping;
//get cart id and check it
$cartid=self::getCart();
return $p->render($basevars);
}
- //cart is ok, now display it
+ //cart is ok, now get the object
$cart = WOWebCart::fromTablecart(WTcart::getFromDB($cartid));
if(!is_a($cart,"WOWebCart")){
//ooops. internal problem
$p=$twig->loadTemplate("carterror.html");
return $p->render($basevars);
}
-
- $p = $twig->loadTemplate("cart.html");
$list=$basevars;
$list["cart"]=$cart;
- // create page
+ //additional info: available shipping types
+ $list["shipping"]=array();
+ $list["forceshipping"]=$WebForceShipping;
+ $sl=WOShipping::fromTableArrayshipping(WTshipping::selectFromDB());
+ foreach($sl as $s)
+ if($s->getcanuse())
+ $list["shipping"][]=$s;
+
+ //display
+ $p = $twig->loadTemplate("cart.html");
return $p->render($list);
}
public function getTime($i){return $this->lang->getTime($i);}
public function getDateTime($i){return $this->lang->getDateTime($i);}
+ public function formatDate($i,$f){return $this->lang->formatDate($i,$f);}
+
public function getName()
{
return 'LangFilter';
}
+}
+class SmokeFilterExtension extends Twig_Extension
+{
+ public function getFilters()
+ {
+ $this->lang=LanguageManager::singleton();
+ return array(
+ 'isObject' => new Twig_Filter_Method($this, 'isObject'),
+ 'isFalse' => new Twig_Filter_Method($this, 'isFalse'),
+ );
+ }
+
+ public function isObject($i){return is_a($i,"WObject");}
+ public function isFalse($i){return $i===false || $i === null;}
+
+ public function getName()
+ {
+ return 'SmokeFilter';
+ }
}
?>
\ No newline at end of file
return $cnt;
}
- /**helper for web UI: returns the name of the field for this event price*/
+ /**helper for web UI: returns the name of the field for this event price, always the configured amount field from WebCart plus an index indicating category and event IDs*/
public function getAmountinputfield()
{
- return WebCart::TicketAmountField."[".$this->prop_pricecategoryid."]";
+ return WebCart::TicketAmountField."[".$this->prop_pricecategoryid.":".$this->prop_eventid."]";
+ }
+
+ /**helper function for web UI: returns the event ID from an amount field index */
+ static public function getEventIdFromAmountIndex($idx)
+ {
+ $sl=explode(":",$idx);
+ if(count($sl==2))return $sl[1]+0;
+ else return null;
+ }
+
+ /**helper function for web UI: returns the event ID from an amount field index */
+ static public function getCategoryIdFromAmountIndex($idx)
+ {
+ $sl=explode(":",$idx);
+ if(count($sl==2))return $sl[0]+0;
+ else return null;
}
};
$twig->addExtension(new $t());
}
$twig->addExtension(new LangFilterExtension);
+$twig->addExtension(new SmokeFilterExtension);
//basic variables shared by all templates
// script URLs
$basevars['script']['this']=$_SERVER['REQUEST_URI'];
$basevars['script']['index']=$_SERVER['SCRIPT_NAME']."?mode=index";
$basevars['script']['eventDetails']=$_SERVER['SCRIPT_NAME']."?mode=eventDetails&event=";
-$basevars['script']['eventOrder']=$_SERVER['SCRIPT_NAME']."?mode=eventOrder&event=";
+$basevars['script']['eventOrder']=$_SERVER['SCRIPT_NAME']."?mode=eventOrder";
$basevars['script']['cart']=$_SERVER['SCRIPT_NAME']."?mode=cart";
$basevars['script']['mycart']=$_SERVER['SCRIPT_NAME']."?mode=mycart";
$basevars['script']['checkout']=$_SERVER['SCRIPT_NAME']."?mode=checkout";
================================
this one is called to show the cart
#}
-
{% extends 'layout.html' %}
{% block title %}Cart{% endblock %}
{% block page %}
- <p style="font-size:40%;">This is cart {{cart.cartid}}</p>
+ {# this just shows what is possible, we do not actually need to tell the user how secury it is...#}
+ <p style="font-size:40%;">This is cart {{cart.cartid}}. It will expire {{cart.timeout|asDateTime}}.</p>
+ {# the isempty flag tells us whether we need to bother with the rest... #}
{% if cart.isempty %}
This cart is empty.
{% else %}
+ {# build a table with all the items of this shopping cart:
+ this is the headline: #}
<table frame="1" border="1">
<tr><td><b>Item</b></td><td><b>Item Price</b></td><td><b>Amount</b></td><td><b>Sum</b></td></tr>
+
+ {# go through tickets and render them #}
{% for ticket in cart.tickets %}
- {% set pricesum=ticket.price * ticket.amount %}
- <tr><td>Ticket: {{ticket.event.title}}</td><td align="right">{{ticket.price|asMoney}}</td><td align="right">{{ticket.amount}}</td><td align="right">{{pricesum|asMoney}}</td></tr>
+ {% set pricesum=ticket.price * ticket.amount %}
+ <tr><td>Ticket: {{ticket.event.title}}</td>
+ <td align="right">{{ticket.price|asMoney}}</td>
+ <td align="right">{{ticket.amount}}</td>
+ <td align="right">{{pricesum|asMoney}}</td></tr>
{% endfor %}
+
+ {# now go through vouchers #}
{% for voucher in cart.vouchers %}
<tr><td>Voucher</td><td align="right">{{voucher.value}}</td><td align="right">1</td><td align="right">{{voucher.value}}</td></tr>
{% endfor %}
- <tr><td>Shipping: {{cart.shipping.description}}</td><td align="right">{{cart.shipping.cost|asMoney}}</td><td align="right">1</td><td align="right">{{cart.shipping.cost|asMoney}}</td></tr>
- <tr><td colspan="3" align="right"><b>Sum:</b></td><td>{{cart.totalsum|asMoney}}</td></tr>
+ {# TODO: go through product items #}
+
+ {# render the shipping option #}
+ {% if cart.shipping|isObject %}
+ <tr><td>Shipping: {{cart.shipping.description}}</td>
+ <td align="right">{{cart.shipping.cost|asMoney}}</td>
+ <td align="right">1</td>
+ <td align="right">{{cart.shipping.cost|asMoney}}</td></tr>
+ {% else %}
+ <tr><td colspan="3">No Shipping</td><td align="right">{{0|asMoney}}</td></tr>
+ {% endif %}
+
+ {# the overall sum of the above items... #}
+ <tr><td colspan="3" align="right"><b>Sum:</b></td><td><b>{{cart.totalsum|asMoney}}</b></td></tr>
+
+ {# end of the table #}
</table>
+
+ {# actions the user may want to take #}
<p><a href="{{script.checkout}}">Checkout</a></p>
+
+ {# end of the non-empty cart... #}
{% endif %}
+ {# in any case: allow the user to shop some more... #}
<p><a href="{{script.root}}">Continue Shopping</a></p>
-{% endblock %}
+{% endblock page %}
================================
this one is called to create the event detail/order page
#}
-
{% extends 'layout.html' %}
{% block title %}{{event.title}}{% endblock %}
{% block page %}
-
+ {# show basic information about the event #}
{{event.start|asDateTime}}, {{event.room|e}}<br/>
Artist: {{event.artist.name|e}}<br/>
{{event.description}}<br/>
- <form action="{{script.eventOrder|safe}}{{event.ID|safe}}" method="POST">
- <table>
- <tr><td>Price:</td><td>Amount:</td></tr>
- {% for price in event.price %}
- {% if price.canuse %}
- <tr><td>{{price.price|asMoney}} ({{price.pricecategory.name}})</td>
- <td><select name="{{price.amountinputfield}}">
- {% set ticketsavailable=price.maxavailable - price.amountticketsblock %}
- {% for i in 0 .. ticketsavailable %}
- <option>{{i}}</option>
- {% endfor %}
- </select></td></tr>
- {% endif %}
- {% endfor %}</table><br/>
- <input type="submit" value="add to cart" />
+
+ {# start the form that allows users to order for an event, the |safe filter tells the Twig parser that the URL should not be mangled #}
+ <form action="{{script.eventOrder|safe}}" method="POST">
+ <table>
+ {# listing pricing options... #}
+ <tr><td>Price:</td><td>Amount:</td></tr>
+ {% for price in event.price %}
+ {# the objects handed over from MagicSmoke are not filtered, so it is important to check whether it makes sense to offer a specific price category #}
+ {% if price.canuse %}
+ {# if usable: show the details of the price category #}
+ <tr><td>{{price.price|asMoney}} ({{price.pricecategory.name}})</td>
+ {# the amountinputfield property of the price contains the name for the form element to be used for each options amount - please use this property instead of hard coded values! #}
+ <td><select name="{{price.amountinputfield}}">
+ {# the following example shows how to make the selection of tickets more fool-proof by using a select and a few inline calculations #}
+ {% set ticketsavailable=price.maxavailable - price.amountticketsblock %}
+ {% for i in 0 .. ticketsavailable %}
+ <option>{{i}}</option>
+ {% endfor %}
+ </select></td></tr>
+ {% endif %}
+ {% endfor %}
+ </table><br/>
+
+ {# end of the form... just add a button... #}
+ <input type="submit" value="add to cart" />
</form>
-{% endblock %}
+{% endblock page %}
{% block page %}
- {% for event in events %}
- <h2>{{event.name}}</h2>
- {{event.start|asDate}} {{event.start|asTime}}, {{event.room}}<br/>
- Artist: {{event.artist.name}}<br/>
- {{event.description}}<br/>
- Price:
- {% for price in event.price %}
- {{price.price|asMoney}} ({{price.pricecategory.name}})
- {% endfor %}<br/>
- <a href="{{script.eventDetails}}{{event.ID}}">details/order tickets</a>
- {% endfor %}
+ {# we use this to show monthly date lines #}
+ {% set olddate = '00' %}
+
+ {# go through all events that MagicSmoke gave us #}
+ {% for event in events %}
+ {# check whether it actually makes sense to show this event, we do not show any events that the user cannot get tickets for (however, we do show those which are sold out) #}
+ {% if event.canuse %}
+ {# show the monthly date line #}
+ {% set newdate = event.start|date('Y.m') %}
+ {% if newdate != olddate %}
+ <table width="80%"><td><td width="10%"><hr/></td>
+ <td width="125px">{{ event.start|date('F - Y') }}</td>
+ <td width="*"><hr/></td></tr></table>
+ {% endif %}
+ {% set olddate = newdate %}
+
+ {# show basic data about the event #}
+ <h2>{{event.name}}</h2>
+ {{event.start|asDateTime}}, {{event.room}}<br/>
+ Artist: {{event.artist.name}}<br/>
+ {{event.description}}<br/>
+
+ {# show pricing options #}
+ Price:
+ {% for price in event.price %}
+ {% if loop.index > 1 %},{% endif %}
+ {{price.price|asMoney}} ({{price.pricecategory.name}})
+ {% endfor %}<br/>
+
+ {# is it sold out? #}
+ {% if event.amountFree == 0 %}
+ <font color="#ff0000">Event sold out.</font><br/>
+ {% else %}
+ {# only very few tickets left? #}
+ {% if event.amountFree <= 5 %}
+ <font color="#ff0000">Event almost sold out.</font><br/>
+ {% endif %}
+ <a href="{{script.eventDetails}}{{event.ID}}">details/order tickets</a>
+ {% endif %}
+
+ {# end of event... #}
+ {% endif %}
+ {% endfor %}
-{% endblock %}
+{% endblock page %}
<head>
<title>{% block title %}{% endblock %} - Magic Smoke Example Layout</title>
-<link rel="stylesheet" type="text/css" href="styles/style.css">
+<link rel="stylesheet" type="text/css" href="styles/style.css"/>
</head>
<body>