1 # UI::ContentEditor - Interchange page/component edit
3 # $Id: ContentEditor.pm,v 2.23 2008-07-09 12:38:22 thunder Exp $
5 # Copyright (C) 2002-2007 Interchange Development Group
6 # Copyright (C) 1996-2002 Red Hat, Inc.
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public
19 # License along with this program; if not, write to the Free
20 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
23 package UI::ContentEditor;
25 $VERSION = substr(q$Revision: 2.23 $, 10);
28 use POSIX qw/strftime/;
31 use Vend::Interpolate;
49 Vend/ContentEditor.pm -- Interchange Page/component edit
53 [component-editor component=search_box ...]
54 [page-editor page=index ...]
58 The Interchange Component and Page editor provides HTML editing support
59 for Interchange pages, components, and templatees.
73 name => 'standard_page_editor',
75 => [ qw/page_title page_banner display_class members_only /],
76 control_fields_meta => {
79 label => errmsg('Page Title'),
83 label => errmsg('Page Banner'),
86 label => errmsg('Display class'),
87 help => errmsg('This overrides the template type with a different display'),
90 label => errmsg('Members only'),
91 help => errmsg('Allows only logged-in users to display the page'),
95 component_fields => [qw/ output /],
96 component_fields_meta => {
98 label => errmsg('Output location'),
99 help => errmsg('Which section of the page the component should go to'),
112 my %Template; # Initialized at bottom of file
121 #::logDebug("called death for $name: " . errmsg(@_));
122 Vend::Tags->error( { set => errmsg(@_), name => $name } );
127 my ($tag, $msg, @args) = @_;
128 #::logDebug("called pain for $tag: " . errmsg($msg,@args));
130 Vend::Tags->warnings(errmsg($msg,@args));
135 my ($name, $thing, $type) = @_;
137 $status = ref($thing) eq $type
140 death($caller, "%s (%s) not a(n) %s", $name, $thing, $type);
147 die("Must have type and name for delete_store, args were: " . join(" ", @_))
148 unless $type and $name;
149 my $store = $Vend::Session->{content_edit} ||= {};
150 $store->{$type} ||= {};
151 delete $store->{$type}{$name};
158 die("Must have type and name for save_store, args were: " . join(" ", @_))
159 unless $type and $name;
160 my $store = $Vend::Session->{content_edit} ||= {};
161 $store->{$type} ||= {};
162 $store->{$type}{$name} = $value;
168 my $store = $Vend::Session->{content_edit} ||= {};
169 return $store unless $type;
170 $store->{$type} ||= {};
171 return $store->{$type} unless $name;
172 return $store->{$type}{$name};
177 return $opt->{component_db} if defined $opt->{component_db};
178 my $tab = $opt->{component_table};
179 $tab ||= $::Variable->{UI_COMPONENT_TABLE};
180 $tab ||= 'component';
181 $opt->{component_db} = ::database_exists_ref($tab) || '';
186 return $opt->{template_db} if defined $opt->{template_db};
187 my $tab = $opt->{template_table};
188 $tab ||= $::Variable->{UI_TEMPLATE_TABLE};
190 $opt->{template_db} = ::database_exists_ref($tab) || '';
195 return $opt->{page_db} if defined $opt->{page_db};
196 my $tab = $opt->{page_table};
197 $tab ||= $::Variable->{UI_PAGE_TABLE};
199 $opt->{page_db} = ::database_exists_ref($tab) || '';
203 my ($ref, $key, $val) = @_;
209 ## This must be non-destructive of $opt, may add keys with component_
210 sub parse_components {
211 my ($wanted, $opt, $components) = @_;
214 sub extract_template {
216 my $opt = shift || {};
219 if ($sref =~ /\nui_(page_template|template_name):\s*(\w+)/) {
221 } elsif ($sref =~ /\@_(\w+)_TOP_\@/) {
224 $tname = $opt->{ui_page_template};
227 #::logDebug("extract_template read template name='$tname'");
231 my $allt = $opt->{_templates} ||= available_templates($opt);
232 #::logDebug("extract_template got all_templates=" . uneval($allt));
234 for my $ref (@$allt) {
235 if($tname and $tname eq $ref->[0]) {
239 next unless is_yes($ref->[3]);
244 $tref ||= $tdef || $allt->[0];
245 #::logDebug("extract_template derived template name=$tref->[0]");
247 $o->{type} = 'template';
248 return read_template($tref->[0], $o);
251 ## This must be non-destructive of $opt, may add keys with component_
253 my ($tref, $opt) = @_;
257 my $type = $tref->{ui_type};
266 #::logDebug("ui_template_layout=$tref->{ui_template_layout}");
267 if(! ref $tref->{ui_template_layout}) {
268 $tref->{ui_template_layout} = [split /\s*,\s*/, $tref->{ui_template_layout}];
270 #::logDebug("ui_template_layout=$tref->{ui_template_layout}");
271 $things = $tref->{ui_template_layout} || [];
274 #::logDebug("looking at thing=$_");
276 push @int, $tref->{$_};
278 elsif($_ eq 'UI_CONTENT') {
279 #::logDebug("thing=$_ is UI_CONTENT");
282 elsif(defined $::Variable->{$_}) {
283 #::logDebug("thing=$_ is Variable");
284 push @int, Vend::Tags->var($_);
286 elsif($tdb and my $row = $tdb->row_hash($_)) {
287 #::logDebug("thing=$_ is Data");
288 push @int, $row->{comp_text};
290 elsif(/^[A-Z][A-Z_0-9]+$/) {
291 #::logDebug("parse_template: thing=$_ is unknown, creating new thing");
292 push @int, qq{<!-- BEGIN $_ -->\n<!-- END $_ -->};
297 PAGE_PICTURE ui_page_picture
300 while ( $tref->{ui_body} =~ s{
301 (<!--+\s+BEGIN\s+(\w+)\s+--+>
303 <!--+\s+END\s+\2\s+--+>)
305 { $allow{uc $2} ? '' : $1 }eixs
310 next unless $allow{$name};
311 $tref->{$allow{$name}} = $value;
317 next unless defined $_;
318 push (@out, {}), next unless $_;
319 $tref->{$things->[$i]} = $_;
324 <!--+ \s+ begin \s+ component \s+ (\w+) \s+ (\w*) \s* --+>
326 <!--+ \s+ end \s+ component \s+ \1 \s+ --+>
328 \[ include \s+ (.*?) file \s*=\s*["'][^"]*/
329 (?:\[control \s+ component \s* )?
355 my $compname = $1 || $5;
358 #::logDebug("all=$all");
360 if($all =~ m{(?:comp[-_]name|default|component)\s*=\s*(['"|])(.*?)\1}is) {
362 $compname =~ s/^\s*\[control\s+component\s+//i;
363 $compname =~ s/\]\s*$//;
365 if($all =~ m{\bgroup\s*=\s*['"\|]?([-\w]+)}) {
372 my @stuff = ($4, $6, $7, $9);
373 #::logDebug("no comptype, stuff is: " . uneval(\@stuff));
374 @stuff = grep $_, @stuff;
375 my $stuff = join "", @stuff;
376 $stuff =~ /\s+(?:class|group)\s*=[\s'"]*(\w+)/i
379 #::logDebug("comptype=$comptype");
380 push @out, { code => $compname, class => $comptype, where => $things->[$i] };
381 push @comp, $compname;
386 $tref->{ui_slots} = \@out;
387 $tref->{ui_display_order} ||= [];
388 #::logDebug("parsed tref=" . uneval($tref));
393 my ($pref, $tref) = @_;
395 my $p = $pref->{ui_slots} || [];
396 my $t = $tref->{ui_slots} || [];
398 #::logDebug("page slots in=" . uneval($p));
399 #::logDebug("tpl slots in=" . uneval($t));
402 #### Temporarily remove content slot
410 for($idx = 0; $idx <= $#$p; $idx++) {
411 next if defined $p and $p->[$idx] and $p->[$idx]{where};
415 if($idx > $#$p and $#$p > 0) {
417 "No content slot found in page %s",
418 $pref->{ui_page_template},
422 $content = splice @$p, $idx, 1;
425 #### Find content slot in template
426 for($idx = 0; $idx <= $#$t; $idx++) {
427 next if defined $t and $t->[$idx] and $t->[$idx]{where};
431 while ($#$p > $#$t) {
435 #::logDebug("splice index=$idx");
436 splice @$p, $idx, 0, $content;
437 #::logDebug("page slots now=" . uneval($p));
439 if($idx > $#$t and $#$t > 0) {
441 "No content slot found in template %s",
442 $pref->{ui_page_template},
446 for(my $i = 0; $i < @$t; $i++) {
447 #::logDebug("Matching slot $i");
448 if (! defined $p->[$i]) {
449 #::logDebug("slot $i not defined?");
450 $p->[$i] = { %{$t->[$i]} };
453 if($p->[$i]{class} ne $t->[$i]{class}) {
454 $p->[$i] = { %{$t->[$i]} };
457 $p->[$i]{where} = $t->[$i]{where};
463 #::logDebug("page slots out=" . uneval($p));
464 #::logDebug("tpl slots out=" . uneval($t));
469 my ($pref, $opt) = @_;
472 #::logDebug("pref ui_body=" . uneval($pref->{ui_body}));
473 my $tmpref = { %$pref };
474 $tmpref->{ui_body} = substr($tmpref->{ui_body},0,50);
475 #::logDebug("begin page ref=" . uneval($tmpref));
477 if(my $otname = $pref->{ui_template_name}) {
478 $pref->{ui_page_template} ||= $otname;
480 my $tpl = $pref->{ui_page_template} || 'none';
481 #::logDebug("parse page $pref->{ui_name}, template=$tpl");
484 my $tref = get_store('template', $tpl);
487 #::logDebug("no tref first try...");
488 my $topt = { %$opt };
491 $topt->{type} = 'template';
492 $tref = read_template($tpl, $topt);
495 #::logDebug("parse page looking for template, got " . uneval($tref));
497 assert('template_reference', $tref, 'HASH')
501 #::logDebug("no tref second try...");
502 pain('read_template', '%s %s not found', 'template', $tpl);
503 $tref = read_template('', { new => 1, type => 'template'});
505 #::logDebug("tref ready to parse: " . uneval($tref));
506 parse_template($tref, $opt);
507 ## Xfer needed template info
508 my $order = $pref->{ui_display_order} = [ @{ $tref->{ui_display_order} } ];
509 #::logDebug("tref order was: " . uneval($order));
511 $pref->{ui_template_layout} = [ @{ $tref->{ui_template_layout} } ];
512 $pref->{ui_page_picture} = $tref->{ui_page_picture};
514 $pref->{$_} = { %{$tref->{$_}} };
517 my $body = delete $pref->{ui_body};
519 #::logDebug("page body=" . uneval($body));
520 unless(defined $body) {
521 ### Already parsed, match slots and leave if not new page
522 match_slots($pref, $tref);
523 #::logDebug("pref now=" . uneval($pref));
524 return unless $opt->{new};
527 my @slots = @{ $pref->{ui_slots} || $tref->{ui_slots} || [] };
529 #$body =~ s/\r\n/\n/g;
539 (<!--+\s+BEGIN\s+(\w+)\s+(?:(\w+)\s+)?--+>
541 <!--+\s+END\s+\2\s+.*?--+>)
543 { $allow{uc $2} ? '' : $1 }eixs
549 next unless $allow{$name};
551 #::logDebug("matched name=$name index=$index");
553 $pref->{$name} = $value;
555 elsif($index =~ /\D/) {
556 if(! $pref->{$name}) {
557 $pref->{$name} = { $index => $value };
559 elsif (! ref $pref->{$name}) {
560 my $tmp = $pref->{$name};
566 elsif (ref ($pref->{$name}) eq 'HASH') {
567 $pref->{$name}{$index} = $value;
571 "bad content pointer reference %s %s %s",
579 if(! $pref->{$name}) {
581 $pref->{$name}[$index] = $value;
583 elsif (! ref $pref->{$name}) {
584 my $tmp = $pref->{$name};
586 $pref->{$name}[0] = $tmp;
587 $pref->{$name}[$index] = $value;
589 elsif (ref ($pref->{$name}) eq 'ARRAY') {
590 $pref->{$name}[$index] = $value;
594 "bad content pointer reference %s %s %s",
603 $pref->{CONTENT} = $body unless $found;
606 if($pref->{CONTROLS}) {
607 $controls = $pref->{CONTROLS};
608 $pref->{COMMENTS} = $body;
615 #::logDebug("controls is $controls");
616 ## All that should be left now is [control] and [set]
621 while($controls =~ s{\[control-set\](.*?)\[/control-set\]}{}is ) {
624 $sets =~ s{\[([-\w]+)\](.*?)\[/\1\]}{ _setref($r, $1, $2) }eisg;
626 push @compnames, $r->{component};
632 #::logDebug("controls is $controls");
633 while($controls =~ s{
644 $scratches->{$2} = $1;
648 if($scratches->{not_editable} and $vals->{not_editable}) {
649 return death('controls', "Not editable page");
653 for($idx = 0; $idx <= $#slots; $idx++) {
654 next if $slots[$idx] and $slots[$idx]{where};
660 "No content slot found in template %s",
661 $pref->{ui_page_template},
665 splice @comp, $idx, 0, '';
668 #::logDebug("#slots=" . scalar(@slots) . "#comp=" . scalar(@comp));
669 for( my $i = 0; $i < @comp; $i++) {
673 or pain('parse_page', "no slot number %s", $i), next;
674 $s->{code} = $r->{component};
675 while( my ($k, $v) = each %$r) {
680 $pref->{ui_slots} = \@slots;
681 $pref->{ui_values} = $vals;
682 $pref->{ui_scratchtype} = $scratches;
684 $tmpref = { %$pref };
685 $tmpref->{CONTENT} = substr($tmpref->{CONTENT},0,50);
686 #::logDebug("Parsed pref=" . ::uneval($tmpref));
693 ui_component_type ui_class
694 ui_template_name ui_name
695 ui_template_description ui_label
696 ui_component_group ui_group
697 ui_component_help ui_help
698 ui_component_label ui_label
701 sub legacy_components {
702 my ($ref, $type) = @_;
704 return if $ref->{ui_name} and $ref->{ui_type} and $ref->{ui_label};
705 while( my($old, $new) = each %leg_remap) {
706 my $tmp = delete $ref->{$old};
707 next if defined $ref->{$new} and length($ref->{$new});
710 $ref->{ui_type} = $type;
711 delete $ref->{ui_template};
716 my ($spec, $opt) = @_;
719 #use vars qw/%CompCache/;
725 my $type = $o->{type} = 'template';
727 my $class = $opt->{class};
730 $db = database_exists_ref($opt->{table}) if $opt->{table};
734 if($spec eq 'none') {
737 ui_type => 'template',
738 ui_label => 'No template',
739 ui_template_version => $::VERSION,
740 ui_template_layout => 'UI_CONTENT',
749 @data = get_content_data($spec,$o);
753 my $tname = $db->name();
754 push @atoms, "select * from $tname";
755 push @atoms, "where code = '$spec'";
756 my $q = join " ", @atoms;
757 my $ary = $db->query({ sql => $q, hashref => 1 });
759 push @data, [ $_->{comp_text}, "$table::$spec" ];
766 "ambiguous %s spec, %s selected. Remaining:\n%s",
769 join(",", map { $_->[1] } @data[1 .. $#data]),
776 if(not $dref = $data[0]) {
777 #::logDebug("no data, and new");
778 $opt->{type} ||= 'page';
779 my $prefix = "ui_$type";
785 "${prefix}_version" => Vend::Tags->version(),
790 $ref->{ui_template_layout} = "${name}_TOP, UI_CONTENT, ${name}_BOTTOM";
794 assert("$type reference", $dref, 'ARRAY')
795 or return death("read_$type", "Component read error for %s", $spec);
796 my ($data, $source) = @$dref;
797 #::logDebug("template data is $data");
800 unless (length($data)) {
801 return death("read_$type", "empty %s: %s", errmsg($type), $source);
804 if($data =~ m{^\s*<\?xml version=.*?>}) {
805 $ref = read_xml_component($data, $source);
806 #::logDebug("Got this from read_xml_component: " . ::uneval($ref));
812 $data =~ m{\[comment\]\s*(ui_.*?)\[/comment\]\s*(.*)}s;
813 my $structure = $1 || '';
814 $ref->{ui_body} = $2;
815 unless ($structure) {
816 return death("read_$type", "bad %s: %s", errmsg($type), $source);
819 my @lines = get_lines($structure);
820 #::logDebug("Got lines from get_lines: " . ::uneval(\@lines));
822 parse_line($_, $ref) for @lines;
823 #::logDebug("Parsed lines: " . ::uneval(\@lines));
825 delete $ref->{_current};
827 if(my $order = $ref->{ui_display_order}) {
829 remap_opts($ref->{$_});
833 $ref->{ui_type} = $type;
834 $ref->{ui_source} = $source;
836 #::logDebug("read tref=" . uneval($ref));
837 legacy_components($ref, $type);
838 #::logDebug("tref after legacy remap=" . uneval($ref));
840 if(! $ref->{ui_name}) {
841 # Compatibility with old templates
842 unless ($ref->{ui_name} = delete $ref->{"ui_${type}_name"}) {
843 return death("read_$type", "%s (%s) must have a name", $type, $source);
846 if($ref->{"ui_$type"} eq 'Yes') {
847 delete $ref->{"ui_$type"};
859 my ($spec, $opt) = @_;
863 my $type = $o->{type} = 'component';
865 my $class = $opt->{class};
868 $db = database_exists_ref($opt->{table}) if $opt->{table};
877 @data = get_content_data($spec, $o);
880 my $tname = $db->name();
882 push @atoms, "select * from $tname";
883 push @atoms, "where code = '$spec'";
884 my $q = join " ", @atoms;
885 my $ary = $db->query({ sql => $q, hashref => 1 });
887 push @data, [ $_->{comp_text}, "$table::$spec" ];
897 "ambiguous %s spec, %s selected. Remaining:\n%s",
900 join(",", map { $_->[1] } @data[1 .. $#data]),
907 if(not $dref = $data[0]) {
908 #::logDebug("no data, and new");
909 $opt->{type} ||= 'page';
910 my $prefix = "ui_$opt->{type}";
916 ui_type => $opt->{type},
918 "${prefix}_version" => Vend::Tags->version(),
920 if($opt->{type} eq 'page') {
921 $ref->{ui_page_template} = $opt->{template};
923 elsif($opt->{type} eq 'template') {
927 $ref->{ui_template_layout} = "${name}_TOP, UI_CONTENT, ${name}_BOTTOM";
932 assert("$type reference", $dref, 'ARRAY')
933 or return death("read_$type", "Component read error for %s", $spec);
934 my ($data, $source) = @$dref;
935 #::logDebug("component data is $data");
938 unless (length($data)) {
939 return death("read_$type", "empty %s: %s", errmsg($type), $source);
942 if($data =~ m{^\s*<\?xml version=.*?>}) {
943 $ref = read_xml_component($data, $source);
944 #::logDebug("Got this from read_xml_component: " . ::uneval($ref));
950 $data =~ m{\[comment\]\s*(ui_.*?)\[/comment\]\s*(.*)}s;
951 my $structure = $1 || '';
952 $ref->{ui_body} = $2;
953 unless ($structure) {
954 return death("read_$type", "bad %s: %s", errmsg($type), $source);
957 my @lines = get_lines($structure);
959 parse_line($_, $ref) for @lines;
961 delete $ref->{_current};
963 if(my $order = $ref->{ui_display_order}) {
965 remap_opts($ref->{$_});
969 $ref->{ui_type} = $type;
970 $ref->{ui_source} = $source;
972 #::logDebug("read cref=" . uneval($ref));
973 legacy_components($ref, $type);
974 #::logDebug("cref after legacy remap=" . uneval($ref));
976 if(! $ref->{ui_name}) {
979 "%s (%s) must have a name",
991 sub get_content_dirs {
996 if($dir = $opt->{dir}) {
999 elsif($opt->{type} eq 'page') {
1000 $dir = $Vend::Cfg->{PageDir};
1003 my $tdir = $opt->{template_dir}
1004 || $::Variable->{UI_TEMPLATE_DIR} || 'templates';
1005 if($opt->{type} eq 'component') {
1006 $dir = $opt->{component_dir}
1007 || $::Variable->{UI_COMPONENT_DIR} || "$tdir/components";
1013 my $tmpdir = $Vend::Cfg->{ScratchDir} || 'tmp';
1014 for(\$tmpdir, \$dir) {
1015 $$_ =~ s!^$Vend::Cfg->{VendRoot}/!!;
1017 $tmpdir .= "/components/$Vend::Session->{id}";
1018 return($dir, $tmpdir) if wantarray;
1022 sub get_content_filenames {
1027 my $dir = get_content_dirs($opt);
1028 #::logDebug("got a dir=$dir for $opt->{type}");
1029 return grep -f $_, glob("$dir/$spec");
1032 sub get_content_data {
1037 for(get_content_filenames($spec, $opt)) {
1038 #::logDebug("Looking at filename $_");
1039 push @data, [ Vend::Util::readfile($_, undef, 0), $_ ];
1042 return @data if wantarray;
1047 my ($dir, $opt) = @_;
1051 $opt->{dir} = $dir if $dir;
1053 my $delim = $opt->{delimiter} || ',';
1055 if( $opt->{templates} ) {
1056 $type = 'templates';
1059 $type = 'components';
1070 if($Vend::caCompCache{$type}) {
1071 $things = $Vend::caCompCache{$type};
1072 $labels = $Vend::clCompCache{$type};
1073 $classes = $Vend::ccCompCache{$type};
1076 if($opt->{templates}) {
1077 $things = available_templates($o);
1080 $things = available_components($o);
1082 $Vend::caCompCache{$type} = $things;
1083 $labels = $Vend::clCompCache{$type} = {};
1084 $classes = $Vend::ccCompCache{$type} = {};
1086 $Vend::clCompCache->{$_->[0]} = $_->[1];
1087 $Vend::ccCompCache->{$_->[0]} = $_->[2];
1092 return $Vend::clCompCache->{$opt->{code}};
1095 if($opt->{structure}) {
1096 $opt->{type} = $opt->{ui_type} = 'component';
1097 return read_component($opt->{code}, $opt);
1100 if ($opt->{show_class}) {
1101 return $Vend::ccCompCache->{$opt->{code}};
1104 ## Default is to return options
1107 if(my $class = $opt->{class}) {
1108 my $re = qr{\b(?:$class|ALL)\b};
1109 my @comps = grep $_->[2] =~ $re, @$things;
1113 unless ($opt->{no_sort}) {
1114 @$things = sort { $a->[1] cmp $b->[1] } @$things;
1118 $_->[1] =~ s/($delim)/'&#' . ord($1) . ';'/ge;
1119 my $def = is_yes($_->[3]) ? '*' : '';
1120 push @out, join "=", $_->[0], "$_->[1]$def";
1122 unshift @out, ($opt->{templates} ? "none=No template" : "=No component")
1123 unless $opt->{no_none};
1124 return join $delim, @out;
1127 sub available_components {
1132 $o->{type} = 'component';
1133 $db = ::database_exists_ref($opt->{table}) if $opt->{table};
1137 @data = get_content_data(undef,$o);
1138 #::logDebug(sprintf("got %d items from get_content_data", scalar(@data)));
1142 my $tname = $db->name();
1143 push @atoms, "select code,comp_text from $tname";
1144 push @atoms, "where comp_type = '$opt->{type}'" if $opt->{type};
1145 push @atoms, "where comp_class = '$opt->{class}'" if $opt->{class};
1146 my $q = join " ", @atoms;
1147 my $ary = $db->query({ sql => $q, hashref => 1 });
1149 push @data, [ $_->{comp_text}, "$table::$_->{code}" ];
1154 for my $dref (@data) {
1155 my $data = \$dref->[0];
1156 my ($name, $label, $class);
1158 $$data =~ /\nui_name:\s*(.+)/
1159 or $$data =~ /\nui_component_name:\s*(.+)/
1160 or $$data =~ /\nui_component:\s*(.+)/
1161 or logDebug("name not found in data: $$data")
1165 $$data =~ /\nui_label:\s*(.+)/
1166 or $$data =~ /\nui_component_label:\s*(.+)/
1167 or $$data =~ /\nui_component_description:\s*(.+)/
1171 $$data =~ /\nui_class:\s*(.+)/
1172 or $$data =~ /\nui_component_type:\s*(.+)/
1173 or $$data =~ /\nui_component_group:\s*(.+)/
1176 push @out, [$name, $label, $class];
1179 return @out if wantarray;
1183 sub available_templates {
1188 $o->{type} = 'template';
1189 $db = ::database_exists_ref($opt->{table}) if $opt->{table};
1193 @data = get_content_data(undef,$o);
1197 my $tname = $db->name();
1198 push @atoms, "select code,comp_text from $tname";
1199 push @atoms, "where comp_type = '$opt->{type}'" if $opt->{type};
1200 push @atoms, "where comp_class = '$opt->{class}'" if $opt->{class};
1201 my $q = join " ", @atoms;
1202 my $ary = $db->query({ sql => $q, hashref => 1 });
1204 push @data, [ $_->{comp_text}, "$table::$_->{code}" ];
1209 for my $dref (@data) {
1210 my $data = \$dref->[0];
1211 my ($name, $label, $class, $default);
1213 $$data =~ /\nui_name:\s*(.+)/
1214 or $$data =~ /\nui_template_name:\s*(.+)/
1215 or $$data =~ /\nui_template:\s*(.+)/
1216 or logDebug("name not found in data: $$data")
1220 $$data =~ /\nui_label:\s*(.+)/
1221 or $$data =~ /\nui_template_label:\s*(.+)/
1222 or $$data =~ /\nui_template_description:\s*(.+)/
1226 $$data =~ /\nui_class:\s*(.+)/
1227 or $$data =~ /\nui_template_type:\s*(.+)/
1228 or $$data =~ /\nui_template_group:\s*(.+)/
1232 $$data =~ /\nui_default:\s*(.+)/
1233 or $$data =~ /\nui_template_default:\s*(.+)/
1236 push @out, [$name, $label, $class, $default];
1238 return @out if wantarray;
1243 my ($structure, $opt) = @_;
1245 $structure =~ s/\s+$//;
1246 my @lines = split /\r?\n/, $structure;
1252 next unless s/\\$//;
1256 last unless defined $found;
1257 if (defined $found) {
1258 my $add = splice @lines, $found + 1, 1;
1259 #::logDebug("Add is '$add', found index=$found");
1260 $lines[$found] .= "\n$add";
1261 #::logDebug("Complete line now is '$lines[$found]'");
1269 my ($line, $ref) = @_;
1272 if($line =~ /^\s*ui_/) {
1273 my ($el, $el_item, $el_data);
1275 ($el, $el_item) = split /\s*:\s*/, $_, 2;
1278 ($el, $el_item, $el_data) = split /\s*:\s*/, $_, 3;
1280 #::logDebug("found el=$el el_item=$el_item el_data=$el_data");
1281 if(! defined $el_data) {
1282 $ref->{$el} = $el_item;
1285 if($el_item eq 'ARRAY') {
1287 assert($el, $ref->{$el}, 'ARRAY')
1289 push @{$ref->{$el}}, [ split /[\s,\0]+/, $el_data ];
1291 if($el_item eq 'HASH') {
1293 assert($el, $ref->{$el}, 'HASH')
1295 my %hash = get_option_hash($el_data);
1296 @{$ref->{$el}}{keys %hash} = values %hash;
1300 elsif ( $line =~ /^(\w+)\s*:\s*(.*)/) {
1301 $ref->{_current} = $1;
1303 $ref->{ui_display_order} ||= [];
1304 push @{$ref->{ui_display_order}}, $ref->{_current};
1306 elsif( $line =~ /^\s+(\w+)\s*:\s*(.*)/s ) {
1307 my ($fn, $fv) = ( lc($1), $2 );
1308 $ref->{$ref->{_current}}{$fn} = $fv;
1314 my ($spec, $opt) = @_;
1317 $db = database_exists_ref($opt->{table}) if $opt->{table};
1325 elsif($spec and ! $db) {
1326 @data = get_content_data($spec, $opt);
1329 my $tname = $db->name();
1331 push @atoms, "select * from $tname";
1332 push @atoms, "where code = '$spec'";
1333 my $q = join " ", @atoms;
1334 my $ary = $db->query({ sql => $q, hashref => 1 });
1336 push @data, [ $_->{comp_text}, "$table::$spec" ];
1345 "ambiguous page spec, %s selected. Remaining:\n%s",
1347 join(",", map { $_->[1] } @data[1 .. $#data]),
1351 my $dref = $data[0];
1356 #::logDebug("no data");
1357 my $prefix = "ui_$type";
1360 ui_type => $opt->{type},
1363 "${prefix}_version" => Vend::Tags->version(),
1366 my $tref = extract_template('', $opt);
1367 assert('template', $tref, 'HASH')
1368 or return death('Not even a default template!');
1369 $ref->{ui_page_template} = $tref->{ui_name};
1373 my ($data, $source) = @{$dref || []};
1374 #::logDebug("read page from source=$source");
1378 my $tref = extract_template($data, $opt);
1379 assert('read_page', $tref, 'HASH')
1380 or return death('read_page', "%s has no %s", $source, errmsg('template'));
1381 $ref->{ui_page_template} = $tref->{ui_name};
1382 #::logDebug("page=$spec template=$ref->{ui_page_template}");
1384 if($data =~ m{^\s*<\?xml version=.*?>}) {
1385 $ref = read_xml_component($data, $source);
1386 #::logDebug("Got this from read_xml_component: " . ::uneval($ref));
1390 $data =~ m{\[comment\]\s*(ui_.*?)\[/comment\]\s*(.*)}s;
1391 my $structure = $1 || '';
1392 $ref->{ui_body} = $2;
1397 ui_page_template: none
1399 $ref->{ui_body} = $data;
1402 my @lines = get_lines($structure);
1403 parse_line($_, $ref) for @lines;
1405 #::logDebug("page=$spec ui_name=$ref->{ui_name} after structure parse");
1407 delete $ref->{_current};
1409 if(my $order = $ref->{ui_display_order}) {
1411 remap_opts($ref->{$_});
1415 #::logDebug("page=$spec ui_name=$ref->{ui_name} after remap_opts");
1417 $ref->{ui_name} ||= $spec;
1418 $ref->{ui_type} = $type;
1419 $ref->{ui_source} = $source;
1423 #::logDebug("page=$spec ui_name=$ref->{ui_name}");
1424 #::logDebug("page read returning: " . uneval($ref));
1428 sub page_component_editor {
1429 my ($name, $pos, $comp, $pref, $opt) = @_;
1431 assert('page reference', $pref, 'HASH')
1434 assert('component reference', $comp, 'HASH')
1437 $name ||= $comp->{code};
1439 #::logDebug("called page_component_editor, name=$name comp=" . ::uneval($comp));
1441 ui_name => $pref->{ui_name},
1442 ui_source => $pref->{ui_source},
1443 ui_page_template => $pref->{ui_page_template},
1444 ui_type => $pref->{ui_type},
1445 ui_content_op => 'modify_component',
1446 ui_content_pos => $pos,
1449 my @fields = 'code';
1451 my $topt = { %$opt };
1452 delete $topt->{dir};
1453 delete $topt->{new};
1454 $topt->{type} = 'component';
1455 my $cref = get_store('component', $name) || read_component($name, $topt);
1457 ref($cref) eq 'HASH'
1460 my $action = Vend::Tags->area($Global::Variable->{MV_PAGE});
1461 $action =~ s/\?.*//;
1462 my $extra = qq{ onChange="
1463 if(check_change() == true) {
1464 this.form.action='$action';
1468 $extra =~ s/\s+/ /g;
1472 passed => Vend::Tags->content_info(),
1473 label => 'Component',
1476 my $label = "$name - " . Vend::Tags->content_info( { code => $name, label => 1});
1477 $label = "<H3 align=center>$label</h3>";
1478 my $value = { code => $name };
1484 my $order = $cref->{ui_display_order} || [];
1485 #return undef unless @$order;
1487 if( my $extra_opt = $Extra_options{$opt->{editor_style}} ) {
1488 my $name = $extra_opt->{name} || 'page_editor';
1489 my $dbopt = $Tag->meta_record('ui_component', $name);
1490 my $ef = $dbopt->{component_fields} || $extra_opt->{component_fields};
1491 unless (ref $ef eq 'ARRAY') {
1492 my @f = grep /\w/, split /[\s,\0]+/, $ef;
1496 my $eo = $extra_opt->{component_fields_meta} || {};
1504 $cref->{$_} = $Tag->meta_record("ui_component::$_", $name)
1506 $cref->{$_} = $eo->{$_} ? { %{ $eo->{$_} } } : {};
1508 if($Tag->if_mm('super')) {
1510 my $url = $Tag->area({
1511 href => 'admin/meta_editor',
1513 item_id=${name}::ui_component::$_
1514 ui_return_to=$Global::Variable->{MV_PAGE}
1515 ui_return_to=ui_name=$cref->{ui_name}
1518 my $anchor = errmsg('meta');
1519 my $title = errmsg('Edit meta');
1520 $cref->{$_}{label} ||= $_;
1521 $cref->{$_}{label} = qq{<a href="$url" title="$title" style="float: right">$anchor</a>$cref->{$_}{label}};
1527 for my $f (@$order) {
1528 #::logDebug("building field $f");
1529 $meta->{$f} = { %{ $cref->{$f} || {} } };
1530 my $lab = $meta->{$f}{label} || $f;
1532 push @fields, "=$lab";
1535 $meta->{$f}{label} = 'value';
1536 $value->{$f} = defined $comp->{$f} ? $comp->{$f} : $meta->{$f}{default};
1538 next if $meta->{$f}{type} and $meta->{$f}{type} !~ /text/i;
1539 my $st = "_scratchtype_$f";
1542 label => 'how to set',
1544 passed => qq{tmpn=Unparsed and temporary,
1545 set=Unparsed and persistent,
1546 tmp=Parsed and temporary,
1547 seti=Parsed and persistent},
1549 $value->{$st} = $comp->{$st};
1552 my $fields = join "\n", @fields;
1554 my $tw = $opt->{table_width} || '100%';
1555 # Have to increment position by one to get the slot number
1562 force_defaults => 1,
1563 form_extra => qq{onSubmit="submitted('slot$p'); silent_submit(this.form)" onReset="submitted('slot$p')"},
1565 href => 'silent/ce_modify',
1566 js_changed => qq{ onChange="changed('slot$p')"},
1568 next_text => 'Save',
1575 ui_data_fields => $fields,
1576 view => 'ui_component',
1578 $options{default_ref} = $value;
1579 $options{item_id} = $name;
1580 return Vend::Tags->table_editor( \%options );
1583 sub page_control_editor {
1584 my ($pref, $opt) = @_;
1585 #::logDebug("called page_control_editor");
1586 assert('page reference', $pref, 'HASH')
1590 ui_name => $pref->{ui_name},
1591 ui_source => $pref->{ui_source},
1592 ui_type => $pref->{ui_type},
1593 ui_content_op => 'modify_control',
1597 assert('page display order', $order = $pref->{ui_display_order}, 'ARRAY')
1601 my @fields = 'code';
1603 if( my $extra_opt = $Extra_options{$opt->{editor_style}} ) {
1604 my $name = $extra_opt->{name} || 'page_editor';
1605 my $dbopt = $Tag->meta_record('ui_control', $name);
1606 my $ef = $dbopt->{control_fields} || $extra_opt->{control_fields};
1607 unless (ref $ef eq 'ARRAY') {
1608 my @f = grep /\w/, split /[\s,\0]+/, $ef;
1612 my $eo = $extra_opt->{control_fields_meta} || {};
1620 $pref->{$_} = $Tag->meta_record("ui_control::$_", $name)
1622 $pref->{$_} = $eo->{$_} ? { %{ $eo->{$_} } } : {};
1624 if($Tag->if_mm('super')) {
1626 my $url = $Tag->area({
1627 href => 'admin/meta_editor',
1629 item_id=${name}::ui_control::$_
1630 ui_return_to=$Global::Variable->{MV_PAGE}
1631 ui_return_to=ui_name=$pref->{ui_name}
1634 my $anchor = errmsg('meta');
1635 my $title = errmsg('Edit meta');
1636 $pref->{$_}{label} ||= $_;
1637 $pref->{$_}{label} = qq{<a href="$url" title="$title" style="float: right">$anchor</a>$pref->{$_}{label}};
1643 for my $f (@$order) {
1644 $meta->{$f} = { %{ $pref->{$f} } };
1645 my $lab = $meta->{$f}{label} || $f;
1647 push @fields, "=$lab";
1650 $meta->{$f}{label} = 'value';
1652 next if $meta->{$f}{type} and $meta->{$f}{type} !~ /text/i;
1653 my $st = "_scratchtype_$f";
1656 label => 'how to set',
1658 passed => qq{tmpn=Unparsed and temporary,
1659 set=Unparsed and persistent,
1660 tmp=Parsed and temporary,
1661 seti=Parsed and persistent,
1663 value => $pref->{ui_scratchtype}{$f},
1667 my $fields = join "\n", @fields;
1669 my $tw = $opt->{table_width} || '100%';
1670 my $p = $pref->{ui_name};
1674 force_defaults => 1,
1675 form_extra => qq{onSubmit="submitted('$p'); silent_submit(this.form)" onReset="submitted('$p')" height="100%"},
1677 href => 'silent/ce_modify',
1678 js_changed => qq{onChange="changed('$p')"},
1680 next_text => 'Save',
1687 ui_data_fields => $fields,
1689 view => 'ui_component',
1691 $options{default_ref} = $pref->{ui_values};
1692 $options{item_id} = $p;
1693 return Vend::Tags->table_editor( \%options );
1696 sub make_control_editor {
1697 my ($w, $r, $overall) = @_;
1699 my $type = $overall->{ui_type} || 'component';
1703 ui_name => $overall->{ui_name},
1704 ui_source => $overall->{ui_source},
1705 ui_type => $overall->{ui_type},
1711 $widopt = {code =>'hiddentext'};
1712 $href = 'silent/ce_modify';
1713 $extra = qq{onSubmit="submitted('$w'); silent_submit(this.form)" onReset="submitted('$w')"};
1714 $hidden->{ui_content_op} = 'modify';
1717 $href = $Global::Variable->{MV_PAGE};
1718 $hidden->{ui_content_op} = 'add';
1724 force_defaults => 1,
1725 form_extra => $extra,
1727 js_changed => 'changed',
1732 table => $::Variable->{UI_META_TABLE} || 'mv_metadata',
1733 view => 'ui_component',
1738 $options{default_ref} = $r;
1739 $options{item_id} = $w;
1740 return Vend::Tags->table_editor( \%options );
1744 my($pref, $opt) = @_;
1746 my @keys = keys %$pref;
1747 my @ui_keys = grep /^ui_/, @keys;
1752 my $comp = $pref->{ui_slots};
1754 $ignore{ui_slots} = 1;
1755 $ignore{ui_display_order} = 1;
1756 $ignore{ui_values} = 1;
1757 $ignore{ui_scratchtype} = 1;
1760 $overall->{safe_data} = 1; # Allow ITL introduction
1761 for(qw/ PREAMBLE CONTENT POSTAMBLE /) {
1762 $overall->{$_} = $pref->{$_};
1766 next if $ignore{$_};
1768 $overall->{$_} = $pref->{$_};
1771 if($pref->{ui_display_order}) {
1772 $overall->{ui_display_order} = join " ", @{$pref->{ui_display_order}};
1775 my $vals = $pref->{ui_values} || {};
1776 my $scratches = $pref->{ui_scratches};
1777 while ( my($k,$v) = each %$vals) {
1778 $overall->{$k} = $v;
1779 $overall->{"_scratchtype_$k"} = $scratches->{$k};
1782 $overall->{_editor_table} = page_control_editor($pref, $opt);
1785 # This is destructive, but slots are rebuilt every time
1786 my $slots = $pref->{ui_slots} || [];
1788 # Need position in case two components are the same
1790 for my $c (@$slots) {
1793 $r->{component} ||= $r->{code};
1794 #::logDebug("slot pos=$pos, slot=" . ::uneval($c));
1795 delete $r->{_editor_table};
1797 my $cname = $r->{component} || '';
1799 if($opt->{page_edit}) {
1800 $r->{_editor_table} = page_component_editor(
1812 ## Allow add of new component
1813 if ($opt->{template_edit}) {
1814 push @tables, { _editor_table => make_control_editor('', {}, $overall) };
1816 #::logDebug("returning overall=" . uneval($overall));
1817 return ($overall, \@tables);
1820 sub template_region {
1821 my($tref, $opt) = @_;
1823 my @keys = keys %$tref;
1824 my @ui_keys = grep /^ui_/, @keys;
1829 my $comp = $tref->{ui_slots};
1830 $ignore{ui_slots} = 1;
1834 for my $reg ( @{$tref->{ui_template_layout}} ) {
1835 my $r = { name => $reg, code => $reg };
1836 if($reg eq 'UI_CONTENT') {
1837 $r->{contents} = "Slot $snum: Page content";
1844 next unless $_->{where} eq $reg;
1845 my $code = $_->{code};
1848 $lab = content_info(undef, { label => 1, code => $code} );
1849 $lab = " default=$lab ($code)";
1851 push @things, "Slot $snum: class=$_->{class}$lab";
1854 $r->{contents} = join "<br>", @things;
1855 $r->{slots} = \@things;
1860 $ignore{ui_display_order} = 1;
1862 my $overall = {%{$opt}};
1863 $overall->{safe_data} = 1; # Allow ITL introduction
1866 next if $ignore{$_};
1868 $overall->{$_} = $tref->{$_};
1874 my $ref = $tref->{$w} or next;
1875 next if $ignore{$w};
1876 if( ref($ref) eq 'HASH' ) {
1879 $wattr{$w}{$_} = $ref->{$_};
1883 $overall->{$w} = $ref;
1887 my $order = $tref->{ui_display_order} || [];
1890 for my $w (@$order) {
1894 if($opt->{template_edit}) {
1895 $r->{_editor_table} = make_control_editor($w, $r, $overall);
1900 $::Scratch->{ce_modify} = '[content-modify]';
1901 ## Allow add of new component
1902 if ($opt->{template_edit}) {
1903 push @tables, { _editor_table => make_control_editor('', {}, $overall) };
1905 return ($overall, \@regions, $comp, \@tables);
1908 sub component_region {
1909 my ($cref, $opt) = @_;
1911 my @keys = keys %$cref;
1912 my @ui_keys = grep /^ui_/, @keys;
1917 my $overall = {%{$opt}};
1918 $overall->{safe_data} = 1; # Allow ITL introduction
1919 $overall->{ui_body} = $cref->{ui_body};
1922 $overall->{$_} = $cref->{$_};
1928 my $ref = $cref->{$w} or next;
1929 next if $ignore{$w};
1930 if( ref($ref) eq 'HASH' ) {
1933 $wattr{$w}{$_} = $ref->{$_};
1937 # Is it ever right to have a scalar or array? I don't think so.
1944 my $order = $cref->{ui_display_order} || [];
1947 for my $w (@$order) {
1951 #::logDebug("table-editor options: " . uneval(\%options));
1952 if($opt->{component_edit}) {
1953 $r->{_editor_table} = make_control_editor($w, $r, $overall);
1958 $::Scratch->{ce_modify} = '[content-modify]';
1959 ## Allow add of new component
1960 if ($opt->{component_edit}) {
1961 push @tables, { _editor_table => make_control_editor('', {}, $overall) };
1964 return($overall, \@tables);
1967 my @valid_attr = qw/
1992 @valid_attr{@valid_attr} = @valid_attr;
2004 my $v = _trim(shift);
2011 my ($ref, $opt) = @_;
2014 $ref->{ui_type} eq $type
2015 or death("publish_$type", "Type must be %s to publish %s", $type, $type);
2016 my $name = $ref->{ui_name}
2017 or death("publish_$type", "Must have name to publish %s", $type);
2019 my $found_something = 0;
2022 if($ref->{PREAMBLE} =~ /\S/) {
2023 push @sets, "<!-- BEGIN PREAMBLE -->";
2024 $ref->{PREAMBLE} =~ s/^\s*\n//;
2025 $ref->{PREAMBLE} =~ s/\n\s*$//;
2026 $ref->{PREAMBLE} =~ s/\r\n|\r/\n/g;
2027 push @sets, $ref->{PREAMBLE};
2028 push @sets, "<!-- END PREAMBLE -->";
2030 delete $ref->{PREAMBLE};
2032 my $vals = delete $ref->{ui_values};
2033 my $scratches = delete $ref->{ui_scratchtype};
2034 my $order = delete $ref->{ui_display_order} || [];
2036 # Do this first to get these things out of reference
2037 # n=name k=key v=value
2038 for my $n (@$order) {
2040 my $stype = $scratches->{$n} || 'tmpn';
2041 my $val = $vals->{$n};
2042 if($opt->{preview} and $n eq $opt->{preview}) {
2043 $val = ($opt->{preview_tag} || '**** PREVIEW ****') . " $val";
2045 push @sets, "[$stype $n]" . $val . "[/$stype]";
2048 $found_something += scalar(@sets);
2050 #::logDebug("publish_page ref=" . ::uneval($ref));
2052 # Things we want every time
2053 my $layout = delete $ref->{ui_template_layout} || [];
2055 #::logDebug("layout=" . ::uneval($layout));
2058 my $slots = delete $ref->{ui_slots} || [];
2059 push @header, "ui_$type: $name";
2060 push @header, "ui_type: $type";
2061 push @header, "ui_name: $name";
2062 push @header, "ui_page_template: $ref->{ui_page_template}";
2063 push @header, "ui_version: " . Vend::Tags->version();
2064 delete $ref->{ui_name};
2065 delete $ref->{ui_type};
2066 delete $ref->{"ui_$type"};
2067 delete $ref->{ui_slots};
2068 delete $ref->{ui_version};
2069 delete $ref->{ui_page_template};
2070 delete $ref->{ui_page_picture};
2071 my $body = delete $ref->{CONTENT};
2072 $body =~ s/\r\n/\n/g;
2075 my @controls = '[control reset=1]';
2077 for my $r (@$slots) {
2078 next unless $r->{where};
2080 push @controls, '[control-set]';
2081 my @order = 'component';
2082 my %seen = qw/ code 1 mv_ip 1 where 1 class 1 component 1 /;
2083 push @order, grep !$seen{$_}++, sort keys %$r;
2086 push @controls, "\t[" . "$_]$r->{$_}" . "[/$_]";
2088 push @controls, '[/control-set]';
2090 push @controls, '[control reset=1]';
2093 for my $var (@$layout) {
2094 if ($var eq 'UI_CONTENT') {
2095 push @bods, "<!-- BEGIN CONTENT -->";
2096 $body =~ s/^\s*\n//;
2097 $body =~ s/\n\s*$//;
2099 push @bods, "<!-- END CONTENT -->";
2101 elsif ($var =~ /^[A-Z]/) {
2103 push @bods, '@_' . $var . '_@';
2106 #::logDebug("bad bod: $var");
2110 if($ref->{POSTAMBLE} =~ /\S/) {
2112 push @bods, "<!-- BEGIN POSTAMBLE -->";
2113 $ref->{POSTAMBLE} =~ s/^\s*\n//;
2114 $ref->{POSTAMBLE} =~ s/\n\s*$//;
2115 $ref->{POSTAMBLE} =~ s/\r\n|\r/\n/g;
2116 push @bods, $ref->{POSTAMBLE};
2117 push @bods, "<!-- END POSTAMBLE -->";
2119 delete $ref->{POSTAMBLE};
2121 return $body unless $found_something;
2123 for(sort keys %$ref) {
2125 my $val = delete $ref->{$_};
2126 next unless length($val);
2127 push @header, "$_: " . trim_format($val);
2131 for(sort keys %$ref) {
2132 # We don't do anything here, don't want junk
2136 my $out = "[comment]\n";
2137 $out .= join "\n", @header;
2138 $out .= "\n[/comment]\n";
2140 $out .= join "\n", @sets;
2142 $out .= join "\n", @controls;
2144 $out .= join "\n", @bods;
2148 sub format_template {
2150 my $type = 'template';
2151 #::logDebug("called format_template name=$ref->{ui_name} type=$ref->{ui_type}");
2152 $ref->{ui_type} eq $type
2153 or death("publish_$type", "Type must be %s to publish %s", $type, $type);
2154 my $name = $ref->{ui_name}
2155 or death("publish_$type", "Must have name to publish %s", $type);
2160 my $order = delete $ref->{ui_display_order} || [];
2162 # Do this first to get these things out of reference
2163 # n=name k=key v=value
2164 for my $n (@$order) {
2165 my $r = delete $ref->{$n};
2167 my $default = defined $r->{default} ? $r->{default} : '';
2169 for my $k (sort keys %$r) {
2170 my $v = trim_format($r->{$k});
2171 $out .= "\t$k: $v\n";
2173 push @controls, $out;
2174 push @sets, "[set $n]" . $default . '[/set]';
2178 # Things we want every time
2180 push @header, "ui_$type: $name";
2181 push @header, "ui_type: $type";
2182 push @header, "ui_name: $name";
2183 push @header, "ui_version: " . Vend::Tags->version();
2184 delete $ref->{ui_name};
2185 delete $ref->{ui_type};
2186 delete $ref->{"ui_$type"};
2187 delete $ref->{ui_slots};
2188 delete $ref->{ui_version};
2189 my $body = delete $ref->{ui_body};
2190 $body =~ s/\r\n/\n/g;
2193 my $dir = $::Variable->{UI_REGION_DIR} || 'templates/regions';
2195 my $layout = delete $ref->{ui_template_layout} || [];
2197 for my $var (@$layout) {
2198 next if $var eq 'UI_CONTENT';
2199 my $thing = delete($ref->{$var});
2202 $r = $Vend::Cfg->{DirConfig}
2203 and $r = $r->{Variable}
2205 and Vend::Tags->write_relative_file($v, $thing)
2207 if(! $regdir and ref($r) eq 'HASH') {
2209 while( ($k, $v) = each %$r) {
2210 last if $k =~ /_(TOP|BOTTOM)$/;
2213 $regdir =~ s:/[^/]+$::;
2216 pain('format_template',
2217 "unable to write dynamic variable, saving %s to $dir",
2221 Vend::Tags->write_relative_file("$regdir/$var", $thing)
2223 death('format_template', "unable to write any dynamic variable, help!");
2224 pain('publish_template', "Must apply changes for access to this template.");
2227 $ref->{ui_template_layout} = join ", ", @$layout;
2229 if(my $pp = delete $ref->{ui_page_picture}) {
2232 $pp =~ s{^\s*<!--+\s*BEGIN PAGE_PICTURE\s*--+>\s*}{};
2233 $pp =~ s{\s*<!--+\s*END PAGE_PICTURE\s*--+>\s*$}{};
2234 $pp = qq{<!-- BEGIN PAGE_PICTURE -->\n$pp\n<!-- END PAGE_PICTURE -->\n};
2238 for(sort keys %$ref) {
2240 my $val = delete $ref->{$_};
2241 next unless length($val);
2242 push @header, "$_: " . trim_format($val);
2246 for(sort keys %$ref) {
2247 # We don't do anything here, don't want junk
2251 my $out = "[comment]\n";
2252 $out .= join "\n", @header;
2254 $out .= join "\n", @controls;
2255 $out .= "\n[/comment]\n";
2256 $out .= join "\n", @sets;
2260 sub format_component {
2262 my $type = 'component';
2263 #::logDebug("format component=" . ::uneval($ref));
2264 $ref->{ui_type} eq $type
2265 or death("publish_$type", "Type must be %s to publish %s", $type, $type);
2266 my $name = $ref->{ui_name}
2267 or death("publish_$type", "Must have name to publish %s", $type);
2271 my $order = delete $ref->{ui_display_order} || [];
2273 # Do this first to get these things out of reference
2274 # n=name k=key v=value
2275 for my $n (@$order) {
2277 next unless $r = delete $ref->{$n};
2279 for my $k (sort keys %$r) {
2280 my $v = trim_format($r->{$k});
2281 $out .= "\t$k: $v\n";
2283 push @controls, $out;
2286 # Things we want every time
2288 push @header, "ui_$type: $name";
2289 push @header, "ui_type: $type";
2290 push @header, "ui_name: $name";
2291 delete $ref->{ui_name};
2292 delete $ref->{ui_type};
2293 delete $ref->{"ui_$type"};
2294 my $body = delete $ref->{ui_body};
2295 $body =~ s/\r\n/\n/g;
2298 for(sort keys %$ref) {
2300 my $val = delete $ref->{$_};
2301 next unless length($val);
2302 push @header, "$_: " . trim_format($val);
2306 for(sort keys %$ref) {
2307 push @header, "$_: " . trim_format(delete $ref->{$_});
2309 my $out = "[comment]\n";
2310 $out .= join "\n", @header;
2312 $out .= join "\n", @controls;
2313 $out .= "\n[/comment]\n";
2318 my ($record, $dest) = @_;
2319 my $dir = $::Variable->{UI_PAGE_DIR} || 'pages';
2320 $dest ||= "$dir/$record->{code}";
2321 Vend::Tags->write_relative_file($dest, $record->{page_text});
2324 sub write_template {
2325 my ($record, $dest) = @_;
2326 my $dir = $::Variable->{UI_TEMPLATE_DIR} || 'templates';
2327 $dest ||= "$dir/$record->{code}";
2328 Vend::Tags->write_relative_file($dest, $record->{temp_text});
2331 sub write_component {
2332 my ($record, $dest) = @_;
2333 my $dir = $::Variable->{UI_COMPONENT_DIR} || 'templates/components';
2334 $dest ||= "$dir/$record->{code}";
2335 Vend::Tags->write_relative_file($dest, $record->{comp_text});
2339 my ($ref, $opt) = @_;
2340 my $vref = $opt->{values_ref} || \%CGI::values;
2341 my $curtime = strftime("%Y%m%d%H%M%S", gmtime() );
2342 my $showdate = Vend::Tags->filter('date_change', $vref->{ui_show_date});
2343 my $expdate = Vend::Tags->filter('date_change', $vref->{ui_expiration_date});
2345 content_class => $ref->{ui_class},
2346 mod_time => strftime("%Y%m%d%H%M%S", gmtime()),
2347 expiration_date => $expdate,
2348 show_date => $showdate,
2349 came_from => $ref->{ui_source},
2350 base_code => $ref->{ui_name},
2351 ### comp_settings => uneval_it($ref),
2352 hostname => $Vend::remote_addr,
2353 mod_user => $Vend::username,
2355 if($curtime lt $showdate or $expdate && $curtime gt $expdate) {
2356 $r->{code} = $r->{base_code} . ".$curtime";
2359 $r->{code} = $r->{base_code};
2365 my ($ref, $opt) = @_;
2366 my $vref = $opt->{values_ref} || \%CGI::values;
2367 my $curtime = strftime("%Y%m%d%H%M%S", gmtime() );
2368 my $showdate = Vend::Tags->filter('date_change', $vref->{ui_show_date});
2369 my $expdate = Vend::Tags->filter('date_change', $vref->{ui_expiration_date});
2371 reftype => $ref->{ui_type},
2372 content_class => $ref->{ui_class},
2373 mod_time => strftime("%Y%m%d%H%M%S", gmtime()),
2374 expiration_date => $expdate,
2375 show_date => $showdate,
2376 came_from => $ref->{ui_source},
2377 base_code => $ref->{ui_name},
2378 ### comp_settings => uneval_it($ref),
2379 hostname => $Vend::remote_addr,
2380 mod_user => $Vend::username,
2382 if($curtime lt $showdate or $expdate && $curtime gt $expdate) {
2383 $r->{code} = $r->{base_code} . ".$curtime";
2386 $r->{code} = $r->{base_code};
2392 my $dir = $Vend::Cfg->{ScratchDir};
2393 $dir =~ s,^$Vend::Cfg->{VendRoot}/,,;
2394 $dir .= "/previews/$Vend::Session->{id}";
2399 my ($ref, $opt) = @_;
2400 my $dest = preview_dir();
2401 $dest .= "/$ref->{ui_name}";
2402 $::Scratch->{tmp_tmpfile} = $dest;
2403 my $tmp = { %$ref };
2404 my $record = ref_content($tmp)
2405 or return death("preview_template", "bad news");
2406 my $text = format_page(
2409 preview => $::Variable->{PAGE_TITLE_NAME} || 'page_title',
2410 preview_tag => errmsg('****PREVIEW****'),
2413 $record->{page_text} = $text;
2414 #::logDebug("header record: " . uneval($record));
2415 write_page($record, $dest);
2419 my ($ref, $opt) = @_;
2420 my $vref = $opt->{values_ref} || \%CGI::values;
2421 my $dest = $vref->{ui_destination};
2424 my $record = ref_content($ref, $opt)
2425 or return death("publish_page", "bad news");
2426 delete_preview_page($ref, $opt);
2427 my $text = format_page($ref);
2428 $record->{page_text} = $text;
2429 write_page($record);
2432 sub publish_template {
2433 my ($ref, $opt) = @_;
2434 my $vref = $opt->{values_ref} || \%CGI::values;
2435 my $dest = $vref->{ui_destination};
2438 my $record = ref_content($ref)
2439 or return death("publish_template", "bad news");
2440 #::logDebug("Got publish_template ref=" . uneval($ref));
2441 my $text = format_template($ref);
2442 $record->{temp_text} = $text;
2443 #::logDebug("header record: " . uneval($record));
2444 delete_store('template', $record->{code});
2445 write_template($record);
2448 sub publish_component {
2449 my ($ref, $opt) = @_;
2450 my $vref = $opt->{values_ref} || \%CGI::values;
2451 my $dest = $vref->{ui_destination};
2454 my $record = ref_content($ref)
2455 or return death("publish_component", "bad news");
2456 my $text = format_component($ref);
2457 $record->{comp_text} = $text;
2458 #::logDebug("publish_component header record: " . uneval($record));
2459 write_component($record);
2462 sub delete_preview_page {
2463 my ($ref, $opt) = @_;
2464 my $dir = preview_dir();
2465 if($ref->{ui_name} and -f "$dir/$ref->{ui_name}") {
2466 unlink "$dir/$ref->{ui_name}";
2471 my ($ref, $opt) = @_;
2472 delete_preview_page($ref, $opt);
2473 my $store = $Vend::Session->{content_edit}
2474 or return death('cancel', 'content store not found');
2475 $store = $store->{$ref->{ui_type}}
2476 or return death('cancel', 'content store not found');
2477 delete $store->{$ref->{ui_name}};
2487 my ($ref, $opt) = @_;
2488 my $vref = $opt->{values_ref} || \%CGI::values;
2490 my $name = $vref->{code}
2491 or return death('code', 'BLANK');
2493 if($illegal{$name}) {
2494 return death('code', 'reserved attribute name: %s', $name);
2497 my @found = grep length($vref->{$_}), @valid_attr;
2498 my %hash = map { $_ => $vref->{$_} } @found;
2499 #::logDebug("add attribute hash: " . uneval(\%hash));
2500 push @{$ref->{ui_display_order} ||= []}, $name;
2501 $ref->{$name} = \%hash;
2505 my ($ref, $opt) = @_;
2506 my $vref = $opt->{values_ref} || \%CGI::values;
2508 my $slots = $ref->{ui_slots};
2509 assert('ui_slots', $slots, 'ARRAY')
2513 for(grep /^slot\d+$/, keys %$vref) {
2517 $slots_in->{$snum} = $vref->{$_};
2520 #::logDebug("slots in=" . ::uneval($slots_in));
2522 for(my $i = 0; $i < @$slots; $i++) {
2523 my $s = $slots->[$i];
2524 #::logDebug("looking at slot $i, slot_in=$slots_in->{$i}, slot code=$s->{component}, slot=" . ::uneval($slots->[$i]) );
2525 next if $s->{component} eq $slots_in->{$i};
2526 #::logDebug("SLOTS ARE DIFFERENT, $s->{code} ne $slots_in->{$i}");
2528 code => $slots_in->{$i},
2529 component => $slots_in->{$i},
2530 class => $s->{class},
2531 where => $s->{where},
2533 $slots->[$i] = $new;
2539 sub modify_component {
2540 my ($ref, $opt) = @_;
2541 my $vref = $opt->{values_ref} || \%CGI::values;
2543 my $pos = $vref->{ui_content_pos};
2545 or return death('ui_content_pos', '%s is BLANK', 'position');
2547 assert('ui_slots', $ref->{ui_slots}, 'ARRAY')
2549 my $comp = $ref->{ui_slots}[$pos]
2550 or return death("Component change", "No component at position %s", $pos);
2551 #::logDebug("changing slot position $pos, comp=" . ::uneval($comp));
2553 my $name = $comp->{component};
2554 my $submitted_name = $vref->{code};
2556 if($name ne $submitted_name) {
2557 my $class = $comp->{class};
2558 my $where = $comp->{where};
2560 code => $submitted_name,
2561 component => $submitted_name,
2565 my $cref = read_component($submitted_name,
2567 type => 'component',
2568 component_dir => $opt->{component_dir},
2571 assert("component $submitted_name", $cref, 'HASH')
2573 #::logDebug("cref=" . uneval($cref));
2574 my $order = $cref->{ui_display_order} || [];
2576 $comp->{$_} = $cref->{$_}{default}
2577 if defined $cref->{$_}{default};
2580 $ref->{ui_slots}[$pos] = $comp;
2584 my @fields = split /[\0,\s]+/, $vref->{mv_data_fields};
2585 my @found = grep defined($vref->{$_}), @fields;
2588 next if $illegal{$_};
2590 $comp->{$_} = $vref->{$_};
2592 #::logDebug("changing slot position $pos, comp now=" . ::uneval($comp));
2597 sub modify_page_control {
2598 my ($ref, $opt) = @_;
2599 my $vref = $opt->{values_ref} || \%CGI::values;
2601 my $name = $vref->{code}
2602 or return death('code', 'BLANK');
2604 my @fields = split /[\0,\s]+/, $vref->{mv_data_fields};
2605 my @found = grep defined($vref->{$_}), @fields;
2606 my $vals = $ref->{ui_values};
2607 my $scratches = $ref->{ui_scratchtype};
2609 assert('page values', $vals, 'HASH')
2612 assert('scratchtypes', $scratches, 'HASH')
2616 next if $illegal{$_};
2618 if(s/^_scratchtype_//) {
2619 $scratches->{$_} = $vref->{$f};
2622 $vals->{$_} = $vref->{$_};
2628 sub modify_attribute {
2629 my ($ref, $opt) = @_;
2630 my $vref = $opt->{values_ref} || \%CGI::values;
2632 my $name = $vref->{code}
2633 or return death('code', 'BLANK');
2635 if($illegal{$name}) {
2636 return death('code', 'reserved attribute name: %s', $name);
2639 my @found = grep length($vref->{$_}), @valid_attr;
2641 my $r = $ref->{$name}
2642 or return death($name, 'Attribute %s not found', $name);
2645 $r->{$_} = $vref->{$_};
2656 ui_destination => 1,
2666 sub modify_top_attribute {
2667 my ($ref, $opt) = @_;
2668 my $vref = $opt->{values_ref} || \%CGI::values;
2670 my $illegal = $illegal_top{$ref->{ui_type}} || $illegal_top{default};
2671 my $always = $always_top{$ref->{ui_type}} || $always_top{default};
2675 #::logDebug("checking $_ ($ref->{$_} -> $vref->{$_}) for legality");
2676 next if $illegal->{$_};
2677 next unless defined $ref->{$_} or $always->{$_};
2678 #::logDebug("$_ is legal and defined in ref (or is always allowed)");
2683 #::logDebug("modifying $_, $ref->{$_} to $vref->{$_}");
2684 $ref->{$_} = $vref->{$_};
2690 my ($ref, $opt) = @_;
2691 my $vref = $opt->{values_ref} || \%CGI::values;
2692 my $code = $vref->{code} || 'ui_body';
2693 #::logDebug("modify body, code=$code");
2694 defined $vref->{ui_body_text}
2697 'Body content not found for %s %s',
2701 length $vref->{ui_body_text}
2704 'Body content for %s defined but had zero length.',
2707 #::logDebug("modified ui_body, length=" . length($vref->{ui_body_text}));
2708 $ref->{$code} = $vref->{ui_body_text};
2712 sub delete_attribute {
2713 my ($ref, $opt) = @_;
2714 my $vref = $opt->{values_ref} || \%CGI::values;
2716 my $name = $vref->{code}
2717 or return death('code', 'BLANK');
2719 my $ary = $ref->{ui_display_order} ||= [];
2724 $found = 1, last if $_ eq $name;
2728 return death('code', 'attribute %s not found', $name)
2731 splice @$ary, $i, 1;
2732 delete $ref->{$name};
2735 sub reorder_attribute {
2736 my ($ref, $opt) = @_;
2737 my $vref = $opt->{values_ref} || \%CGI::values;
2739 my $name = $vref->{code}
2740 or return death('code', 'BLANK');
2742 my $direc = $vref->{ce_motion}
2743 or pain('ce_motion', 'No direction specified, defaulting to %s', 'up');
2744 if($direc eq 'down') {
2751 my $ary = $ref->{ui_display_order} ||= [];
2756 $found = 1, last if $_ eq $name;
2760 return death('code', 'attribute %s not found', $name)
2763 my $new = $idx + $direc;
2765 return death('ce_motion', 'cannot move %s from %s', 'up', 'first');
2767 elsif($new >= @$ary) {
2768 return death('ce_motion', 'cannot move %s from %s', 'down', 'last');
2770 my $src = $ary->[$idx];
2771 $ary->[$idx] = $ary->[$new];
2772 $ary->[$new] = $src;
2773 @$ary = grep defined $_, @$ary;
2778 my %immediate_action = (
2780 delete $Vend::Session->{content_edit};
2781 File::Path::rmtree(preview_dir());
2785 my %common_action = (
2786 cancel => \&cancel_edit,
2787 motion => \&reorder_attribute,
2788 modify_top => \&modify_top_attribute,
2789 modify => \&modify_attribute,
2790 modify_body => \&modify_body,
2791 delete => \&delete_attribute,
2792 add => \&add_attribute,
2795 my %specific_action = (
2797 preview => \&preview_page,
2798 publish => \&publish_page,
2799 modify => \&modify_top_attribute,
2800 modify_control => \&modify_page_control,
2801 modify_component => \&modify_component,
2802 modify_slots => \&modify_slots,
2805 publish => \&publish_template,
2808 publish => \&publish_component,
2812 sub content_modify {
2813 my($ops, $name, $type, $opt) = @_;
2816 my $vref = $opt->{values_ref} || \%CGI::values;
2817 $ops ||= $vref->{ui_content_op};
2820 if($sub = $immediate_action{$ops}) {
2821 #::logDebug("content modify immediate action");
2822 return $sub->(undef, $opt);
2824 #::logDebug("content_modify: called, name=$name type=$type ops=$ops");
2826 $type ||= $vref->{ui_type}
2827 or return death('ui_type', "Must specify a type");
2829 $name ||= $vref->{ui_name}
2830 or return death('ui_name', "Must specify a name for %s", $type);
2831 #::logDebug("content_modify: called, name=$name type=$type");
2833 my(@ops) = split /[\s,\0]+/, $ops;
2834 #::logDebug("content_modify: ops=" . join(",", @ops) . " vref=$vref");
2836 my $ref = get_store($type,$name)
2837 or return death('content_modify', "%s %s not found", $type, $name);
2839 #in case of an alternative component name
2840 if ($vref->{ui_destination} ne "") {
2841 $name = $ref->{ui_name} = $vref->{ui_destination};
2845 foreach my $op (@ops) {
2846 #::logDebug("content_modify: doing name=$name type=$type op=$op");
2847 #::logDebug("content_modify: doing name=$name type=$type op=$op ref=" . uneval($ref));
2849 $sub = $specific_action{$type}{$op} || $common_action{$op};
2852 return death('ui_content_op', "%s %s not found", 'operation', $op );
2855 #::logDebug("ref before modify, code=$vref->{code}=" . uneval($ref));
2856 if(! $sub->($ref, $opt) ) {
2857 pain('content_modify', "op %s failed for %s %s.", $op, $type, $name);
2859 #::logDebug("ref AFTER modify=, code=$vref->{code}" . uneval($ref));
2866 my($name, $opt, $form_template) = @_;
2870 $opt->{page_edit} = 1;
2872 #::logDebug("in page_editor, name=$name");
2873 my $pref = read_page($name, $opt);
2875 if(ref($pref) ne 'HASH') {
2876 return death('page_editor', "Invalid page: %s", uneval($pref));
2879 $pref = get_store('page', $name) || $pref;
2882 save_store('page', $name, $pref);
2884 parse_page($pref, $opt);
2886 publish_page($pref, $opt) if $opt->{new};
2888 #::logDebug("found a template name=$pref->{ui_name} store=$name: " . uneval($pref));
2890 my ($overall, $comp) = page_region($pref, $opt);
2893 $opt->{list_prefix} || 'pages',
2894 $opt->{prefix} || 'page',
2896 $opt->{comp_list_prefix} || 'components',
2897 $opt->{comp_prefix} || 'comp',
2901 my $bref = $form_template ? \$form_template : \$Template{page_standard} ;
2902 #::logDebug("table-editor records: " . uneval($pref));
2903 #::logDebug("table-editor overall: " . uneval($overall));
2904 return run_templates($overall, $to_run, $bref);
2907 sub template_editor {
2908 my($name, $opt, $form_template) = @_;
2909 #::logDebug("template editor called, name=$name, opt=" . uneval($opt));
2912 $opt->{template_edit} = 1;
2914 my $tref = get_store('template', $name) || read_template($name, $opt);
2915 save_store('template', $name, $tref);
2917 parse_template($tref, $opt);
2919 my ($overall, $reg, $comp, $cont) = template_region($tref, $opt);
2921 $opt->{list_prefix} || 'templates',
2922 $opt->{prefix} || 'tem',
2924 $opt->{region_list_prefix} || 'regions',
2925 $opt->{region_prefix} || 'reg',
2927 $opt->{comp_list_prefix} || 'components',
2928 $opt->{comp_prefix} || 'comp',
2930 $opt->{cont_list_prefix} || 'controls',
2931 $opt->{cont_prefix} || 'cont',
2935 my $bref = $form_template ? \$form_template : \$Template{template_standard} ;
2936 #::logDebug("table-editor records: " . uneval($tref));
2937 #::logDebug("table-editor overall: " . uneval($overall));
2938 return run_templates($overall, $to_run, $bref);
2941 sub component_editor {
2942 my($name, $opt, $form_template) = @_;
2943 #::logDebug("component editor called, cref=$cref opt=" . uneval($opt));
2945 $opt->{component_edit} = 1;
2947 my $cref = read_component($name, $opt);
2948 if(! ref($cref) eq 'HASH') {
2949 return death('component_editor', "Invalid component: %s", uneval($cref));
2952 $cref = get_store('component', $name) || $cref;
2954 save_store('component', $name,$cref);
2956 my ($overall, $tref) = component_region($cref, $opt);
2957 my $to_run = ['components', 'comp', $tref];
2959 my $bref = $form_template ? \$form_template : \$Template{component_standard} ;
2960 #::logDebug("table-editor records: " . uneval($tref));
2961 #::logDebug("table-editor overall: " . uneval($overall));
2962 return run_templates($overall, $to_run, $bref);
2965 my %display_remap = qw/
2973 $name = shift and $opt->{name} = $name;
2974 while( my($k,$v) = each %display_remap ) {
2975 delete $opt->{$v}, next if defined $opt->{$k};
2976 next unless defined $opt->{$v};
2977 $opt->{$k} = delete $opt->{$v};
2983 my ($opt, $to_run, $bref) = @_;
2984 #::logDebug("event_array=" . ::uneval($to_run));
2995 my @things = @$to_run;
2996 while( $region = shift @things ) {
2997 $prefix = shift @things;
2998 $ary = shift @things;
3000 delete $todo{$region};
3001 #::logDebug("run_template region=$region prefix=$prefix ary=$ary from=$opt->{from_session}");
3002 $region =~ s/[-_]/[-_]/g;
3004 next unless $$bref =~ m{\[$region\](.*?)\[/$region\]}is;
3007 if( $run !~ /\S/ or (! $ary and $run !~ /no[-_]match\]/i) ) {
3008 $$bref =~ s{\[$region\](.*?)\[/$region\]}{}sgi;
3011 $opt->{prefix} = $prefix;
3014 matches => scalar(@$ary),
3015 mv_matchlimit => $opt->{ml} || 100,
3017 $$bref =~ s{\[$region\](.*?)\[/$region\]}
3018 {Vend::Interpolate::region($opt, $1)}eisg;
3021 $$bref =~ s,\[$region\](.*?)\[/$region\],,igs;
3027 my ($item, $opt, $form_template) = @_;
3029 $::Scratch->{ce_modify} = '[content-modify]';
3030 if($opt->{type} eq 'page') {
3031 return page_editor($opt->{name}, $opt, $form_template);
3033 elsif ($opt->{type} eq 'template') {
3034 return template_editor($opt->{name}, $opt, $form_template);
3036 elsif ($opt->{type} eq 'component') {
3037 return component_editor($opt->{name}, $opt, $form_template);
3040 return errmsg("Don't know how to edit type '%s'.\n", $opt->{type});
3044 $Template{component_standard} = <<'EOF';
3047 $Template{page_standard} = <<EOF;
3048 <script language=JavaScript>
3049 function visible (index) {
3050 var vis = new Array;
3053 var selnam = 'dynform' + index;
3055 for( xi = 1; ; xi++) {
3056 nam = 'dynform' + xi;
3057 var el = document.getElementById(nam);
3058 if(el == undefined) break;
3060 el.style.visibility = 'Hidden';
3063 var element = document.getElementById(selnam);
3064 element.style.visibility = 'Visible';
3069 <FORM METHOD=POST ACTION="[editor-param url]" ENCTYPE="[editor-param enctype]">
3070 <table width="[editor-param table_width]" [editor-param table_extra]>
3072 <td width="[editor-param left_width]" [editor-param left_extra]>
3076 <li> <A HREF="javascript:void(0)"
3077 onClick="visible([clink-increment])"
3078 >[clink-param label]</A></li>
3087 Left:0; Top:0; Height:504; Width:404;
3096 Left:0; Top:0; Height:504; Width:404;
3099 background-color: [cmenu-param bordercolor]
3105 Left:2; Top:2; Height:500; Width:400;
3108 background-color: [cmenu-param bgcolor]
3114 id=dynform[loop-code]
3117 Left:2; Top:2; Width:300; Height: 300;
3118 Visibility:[loop-change 1][condition]1[/condition]Visible[else]Hidden[/else][/loop-change 1];
3121 >Element [loop-code] <select name=dynform[loop-code]widget>
3123 <OPTION [selected cgi=1 name=dynform[loop-code]widget value=B]>B
3124 <OPTION [selected cgi=1 name=dynform[loop-code]widget value=C]>C
3135 <td width="[editor-param left_width]" [editor-param left_extra]>
3138 <h2>Content edit</h2>
3140 [if-content-param label]<h2>[content-param label]<br>[/if-content-param]
3142 name="[content-var]"
3143 ROWS="[content-param vsize]" COLS="[content-param hsize]">
3158 sub write_xml_component {
3161 return undef unless ref($c) eq 'HASH';
3165 for(qw/component template page/) {
3166 #::logDebug("check for component type=$_");
3167 if(exists $c->{"ui_$_"}) {
3172 #::logDebug("component type=$type");
3175 logError("unrecognized template:\n%s", uneval($c) );
3178 my $out = qq{<?xml version="1.0"?>\n};
3180 my $body = delete $c->{ui_body};
3181 my $order = delete $c->{ui_display_order} || [];
3182 delete $c->{ui_definition};
3184 my @keys = keys %$c;
3185 my @ui_keys = grep /^ui_/, @keys;
3192 my $val = delete $c->{$_};
3193 if($_ eq "ui_$type") {
3194 $cattr{name} ||= $c->{$_};
3203 my @ao; # attributes out
3204 while (my ($k, $v) = each %cattr) {
3205 HTML::Entities::encode($v);
3206 push @ao, qq{$k="$v"};
3209 $out .= join " ", @ao;
3217 my $ref = $c->{$w} or next;
3218 next unless ref($ref) eq 'HASH';
3221 $wattr{$w}{$_} = $ref->{$_};
3225 for my $w (@$order) {
3226 $out .= qq{\t<attr name="$w">\n};
3227 for(keys %{$wattr{$w}} ) {
3228 $out .= "\t\t<$_>$wattr{$w}{$_}</$_>\n";
3230 $out .= qq{\t</attr>\n};
3233 HTML::Entities::encode($body);
3234 $out .= "\t<body>$body</body>\n";
3235 $out .= "</$type>\n";
3240 sub read_xml_component {
3241 my ($thing, $source) = @_;
3243 require XML::Parser;
3246 $thing =~ m{\[comment\]\s*(.*?)\[/comment\](.*)}s
3250 HTML::Entities::encode($body) if $body;
3254 $xml =~ s:<body>ENCODED</body>:<body>$body</body>:;
3255 my $p = new XML::Parser Style => 'Tree';
3259 $tree = $p->parse($xml);
3265 my %recognized = qw/ component 1 template 1 page 1/;
3266 my $type = shift @$tree;
3268 if(! $recognized{$type}) {
3269 logError("unrecognized template type '%s'", $type);
3273 my $ref = shift @$tree;
3274 my $comphash = shift @$ref;
3278 "ui_$type" => $comphash->{name} || 'Yes',
3279 ui_display_order => [],
3280 "ui_${type}_source" => $source,
3283 while (my ($k, $v) = each %$comphash ) {
3284 $el->{"ui_$type" . "_$k"} = $v;
3287 my %get = ( attr => 1, body => 1 );
3289 while( my($t, $v) = splice(@$ref, 0, 2) ) {
3290 #Debug("found param=$t");
3292 if(! defined $get{$t} ) {
3293 logError('%s: unrecognized %s element %s', 'xml_component_read', $type, $t);
3296 my $hash = shift @$v;
3297 my $name = $hash->{name} || 'unknown';
3298 push @{$el->{ui_display_order}}, $name;
3300 while( my ($setting, $ary) = splice(@$v, 0, 2) ) {
3301 next unless $setting;
3302 $el->{$name}{$setting} = $ary->[2];
3305 elsif ($t eq 'body') {
3306 $el->{ui_body} = $v->[2];