Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add [zoom] tag for more sensible templating.
  • Loading branch information
racke committed Aug 14, 2010
1 parent f8df89f commit 8a5100c
Show file tree
Hide file tree
Showing 2 changed files with 220 additions and 0 deletions.
209 changes: 209 additions & 0 deletions lib/WellWell/Zoom.pm
@@ -0,0 +1,209 @@
# WellWell::Zoom - WellWell template parsing routines
#
# Copyright (C) 2010 Stefan Hornburg (Racke) <racke@linuxia.de>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.

package WellWell::Zoom;

use strict;
use warnings;

use XML::Twig;
use Rose::DB::Object::QueryBuilder qw(build_select);

use Vend::Config;
use Vend::Data;
use Vend::Tags;

Vend::Config::parse_tag('UserTag', 'zoom MapRoutine WellWell::Zoom::zoom');
Vend::Config::parse_tag('UserTag', 'zoom Order function name');
Vend::Config::parse_tag('UserTag', 'zoom AddAttr');
Vend::Config::parse_tag('UserTag', 'zoom HasEndTag');

# zoom - [zoom] tag

sub zoom {
my ($function, $name, $opt, $body) = @_;

if ($function eq 'init') {
unless (exists $::Scratch->{zoom}) {
Vend::Tags->tmp('zoom', {});
}

$::Scratch->{zoom}->{$name}->{html} = $body;
$::Scratch->{zoom}->{$name}->{object} = parse_template($body, $opt->{specs});
}

if ($function eq 'display') {
my ($sref, $bref, $sql, $sth, $bind, %columns, $row, $key, $value, $lel, %paste_pos, $rep_str);

unless ($sref = $::Scratch->{zoom}->{$name}->{object}) {
die "Missing template $name\n";
}

$bref = $opt->{build};
$bref->{dbh} = database_exists_ref('products')->dbh();
$bref->{query_is_sql} = 1;

# now determine which fields are needed
while (($key, $value) = each %{$sref->{params}->{$name}->{hash}}) {
push @{$bref->{columns}->{$value->{table}}}, $key;
}

($sql, $bind) = build_select(%$bref);

$sth = $bref->{dbh}->prepare($sql);
$sth->execute(@$bind);

$lel = $sref->{lists}->{$name}->[0]->{elts}->[0];

if ($lel->is_last_child()) {
%paste_pos = (last_child => $lel->parent());
}
else {
%paste_pos = (before => $lel->next_sibling());
}

$lel->cut();

while ($row = $sth->fetchrow_hashref) {
# now fill in params
while (($key, $value) = each %{$sref->{params}->{$name}->{hash}}) {
$rep_str = $row->{$key};

if ($value->{subref}) {
$rep_str = $value->{subref}->($row);
}

if ($value->{filter}) {
$rep_str = Vend::Tags->filter({op => $value->{filter}, body => $rep_str});
}

for my $elt (@{$value->{elts}}) {
if ($elt->{zoom_rep_att}) {
# replace attribute instead of embedded text (e.g. for <input>)
$elt->set_att($elt->{zoom_rep_att}, $rep_str);
}
elsif ($elt->{zoom_rep_elt}) {
# use provided text element for replacement
$elt->{zoom_rep_elt}->set_text($rep_str);
}
else {
$elt->set_text($rep_str);
}
}
}

# now add to the template
my $subtree = $lel->copy();

$subtree->paste(%paste_pos);
}

# replacements for simple values
while (($key, $value) = each %{$sref->{values}}) {
for my $elt (@{$value->{elts}}) {
if ($value->{scope} eq 'scratch') {
$rep_str = $::Scratch->{$key};
}

if ($value->{filter}) {
$rep_str = Vend::Tags->filter({op => $value->{filter}, body => $rep_str});
}

$elt->set_text($rep_str);
}
}

return $sref->{xml}->sprint;
}
}

# parse_template - Parse (HTML) template according to specifications

sub parse_template {
my ($template, $specs) = @_;
my ($twig, $xml, $object);

$object = {specs => $specs, lists => {}, params => {}};

$twig = new XML::Twig (twig_handlers => {_all_ => sub {parse_handler($_[1], $object)}});
$xml = $twig->safe_parse($template);

unless ($xml) {
die "Invalid HTML template: $template\n";
}

$object->{xml} = $xml;
return $object;
}

# parse_handler - Callback for HTML elements

sub parse_handler {
my ($elt, $sref) = @_;
my ($gi, $class, $name, $sob, $elt_text);

$gi = $elt->gi();
$class = $elt->att('class');

if (defined $class && exists $sref->{specs}->{class}->{$class}) {
$sob = $sref->{specs}->{class}->{$class};
$name = $sob->{name} || $class;

if ($sob->{type} eq 'list') {
$sob->{elts} = [$elt];

$sref->{lists}->{$name} = [$sob];
}
elsif ($sob->{type} eq 'param') {
push (@{$sob->{elts}}, $elt);

if ($gi eq 'input') {
# replace value attribute instead of text
$elt->{zoom_rep_att} = 'value';
}
elsif (! $elt->contains_only_text()) {
# contains real elements, so we have to be careful with
# set text and apply it only to the first PCDATA element
if ($elt_text = $elt->first_child('#PCDATA')) {
$elt->{zoom_rep_elt} = $elt_text;
}
}

if ($sob->{sub}) {
# determine code reference for named function
$sob->{subref} = $Vend::Cfg->{Sub}{$sob->{sub}} || $Global::GlobalSub->{$sob->{sub}};
}

$sref->{params}->{$sob->{list}}->{hash}->{$name} = $sob;
push(@{$sref->{params}->{$sob->{list}}->{array}}, $sob);
}
elsif ($sob->{type} eq 'value') {
push (@{$sob->{elts}}, $elt);
$sref->{values}->{$name} = $sob;
}
else {
next;
}
}

return $sref;
}


1;
11 changes: 11 additions & 0 deletions plugins/wishlists/code/wishlist.tag
Expand Up @@ -233,6 +233,17 @@ sub {

my $sql = qq{select sku$fields from cart_products where cart = $wishlist_code order by position};

if ($opt->{zoom}) {
my %build;

%build = (tables => ['cart_products', 'products'],
columns => {cart_products => ['cart']},
clauses => ['T1.sku = T2.sku'],
query => [cart => $wishlist_code]);

return $Tag->zoom({function => 'display', name => 'wishlist', build => \%build});
}

return $Tag->query ({sql => $sql, list => 1, prefix => 'item',
body => $body});
}
Expand Down

0 comments on commit 8a5100c

Please sign in to comment.