<?php
	/**************************************************************************\
	* phpGroupWare - eTemplates - XUL/XML Import & Export                      *
	* http://www.phpgroupware.org                                              *
	* Written by Ralf Becker <RalfBecker@outdoor-training.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.                                              *
	\**************************************************************************/

	/* $Id$ */

	include(PHPGW_SERVER_ROOT . '/etemplate/inc/xmltools.php');


	class xul_io
	{
		var $widget2xul;
		var $attr2xul;
		var $xul2widget;

		function xul_io()
		{
			$this->attr2xul = array(	// how to translate attr, common to all widgets
				'name' => 'id',
				'help' => 'statustext',
				'span' => 'span,class',
				'type' => '',	// this is the widget-name => dont write as attr
				'disabled' => 'disabled=true',
				'readonly' => 'readonly=true',
				'size' => 'options'
			);
			$this->widget2xul = array(	// how to translate widget-names and widget-spec. attr.
				'label' => array(
					'.name' => 'description',
					'label' => 'value'
				),
				'text' => array(
					'.name' => 'textbox',
					'size' => 'size,maxlength'
				),
				'textarea' => array(
					'.name' => 'textbox',
					'.set' => 'multiline=true',
					'size' => 'cols,rows'
				),
				'integer' => array(
					'.name' => 'textbox',
					'.set' => 'type=integer',
					'size' => 'min,max,size'
				),
				'float' => array(
					'.name' => 'textbox',
					'.set' => 'type=float',
					'size' => 'min,max,size'
				),
				'select' => array(
					'.name' => 'menulist,menupopup',
				),
				'select-multi' => array(	// multiselection, if size > 0
					'.name' => 'listbox',
					'size'  => 'rows'
				),
				'template' => array(
					'.name' => 'grid',
					'size'  => 'content'
				),
				'image'   => array(
					'.name' => 'image',
					'label' => 'src'
				),
				'tab' => array(
					'.name' => 'tabbox,tabs,tabpanels'
				),
				'button' => array(
					'.name' => 'button',
					'size'  => 'image,ro_image'
				)
			);
			$this->xul2widget = array(
				'menupopup' => 'select',
				'listbox' => 'select',
				'description' => 'label'
			);
		}

		function set_attributes(&$widget,$attr,$val,$spanned='')
		{
			if ($attr != '')
			{
				$attrs = explode(',',$attr);

				if (count($attrs))
				{
					$vals = count($attrs) > 1 ? explode(',',$val) : array($val);
					while (list($n,$attr) = each($attrs))
					{
						if ($val = $vals[$n])
						{
							if ($attr == 'span')
							{
								$spanned = $val == 'all' ? 999 : $val - 1;
							}
							list($attr,$set) = explode('=',$attr);
							$widget->set_attribute($attr,$set != '' ? $set : $val);
						}
					}
				}
			}
		}

		function export($etempl)
		{
			if ($this->debug)
			{
				echo "<p>etempl->data = "; _debug_array($etempl->data);
			}
			$doc = new xmldoc();

			$xul_overlay = new xmlnode('overlay');

			$xul_grid = new xmlnode('grid');
			$xul_grid->set_attribute('id',$etempl->name);
			$xul_grid->set_attribute('template',$etempl->template);
			$xul_grid->set_attribute('lang',$etempl->lang);
			$xul_grid->set_attribute('group',$etempl->group);
			$xul_grid->set_attribute('version',$etempl->version);
			$this->set_attributes($xul_grid,'width,height,border,class,spacing,padding',$etempl->size);

			$xul_columns = new xmlnode('columns');
			$xul_rows = new xmlnode('rows');

			reset($etempl->data);
			list(,$opts) = each ($etempl->data); // read over options-row
			while (list($r,$row) = each ($etempl->data))
			{
				$xul_row = new xmlnode('row');
				$this->set_attributes($xul_row,'class,valign',$opts["c$r"]);
				$this->set_attributes($xul_row,'height',$opts["h$r"]);

				$spanned = 0;
				while (list($c,$cell) = each($row))
				{
					if ($r == '1')	// write columns only once in the first row
					{
						$xul_column = new xmlnode('column');
						$this->set_attributes($xul_column,'width',$opts[$c]);
						$xul_columns->add_node($xul_column);
					}
					if ($spanned)
					{
						--$spanned;
						continue;	// spanned cells are not written
					}
					$type = $cell['type'];
					if (is_array($type))
					{
						list(,$type) = each($type);
					}
					if (substr($type,0,6) == 'select')
					{
						$type = $cell['size'] > 1 ? 'select-multi' : 'select';
					}
					$widgetattr2xul = isset($this->widget2xul[$type]) ? $this->widget2xul[$type] : array();
					$type = isset($widgetattr2xul['.name']) ? $widgetattr2xul['.name'] : $type;
					list($parent,$child,$child2) = explode(',',$type);
					$widget = new xmlnode($parent);
					if ($child)
					{
						$child = new xmlnode($child);
					}
					if ($child2)
					{
						$child2 = new xmlnode($child2);
					}
					if (isset($widgetattr2xul['.set']))	// set default-attr for type
					{
						$attrs = explode(',',$widgetattr2xul['.set']);
						while (list(,$attr) = each($attrs))
						{
							list($attr,$val) = explode('=',$attr);
							$widget->set_attribute($attr,$val);
						}
					}
					switch ($parent)
					{
					case 'tabbox':
						$labels = explode('|',$cell['label']);  unset($cell['label']);
						$helps  = explode('|',$cell['help']);   unset($cell['help']);
						$names  = explode('|',$cell['name']);   unset($cell['name']);
						for ($n = 0; $n < count($labels); ++$n)
						{
							$tab = new xmlnode('tab');
							$tab->set_attribute('label',$labels[$n]);
							$tab->set_attribute('statustext',$helps[$n]);
							$child->add_node($tab);

							$grid = new xmlnode('grid');
							$grid->set_attribute('id',$names[$n]);
							$child2->add_node($grid);
						}
						break;
					case 'menulist':	// id,options belongs to the 'menupopup' child
						$child->set_attribute('id',$cell['name']); unset($cell['name']);
						if (isset($cell['size']) && $cell['size'] != '')
						{
							$child->set_attribute('options',$cell['size']); unset($cell['size']);
						}
						if ($cell['type'] != 'select')	// one of the sub-types
						{
							$child->set_attribute('type',$cell['type']);
						}
						break;
					case 'menulist':
						if ($cell['type'] != 'select')	// one of the sub-types
						{
							$widget->set_attribute('type',$cell['type']);
						}
						break;
					}
					while (list($attr,$val) = each($cell))
					{
						if (is_array($val))	// correct old buggy etemplates
						{
							list(,$val) = each($val);
						}
						if (isset($widgetattr2xul[$attr]))
						{
							$attr = $widgetattr2xul[$attr];
						}
						elseif (isset($this->attr2xul[$attr]))
						{
							$attr = $this->attr2xul[$attr];
						}
						$this->set_attributes($widget,$attr,$val,&$spanned);
					}
					if ($child)
					{
						$widget->add_node($child);
					}
					if ($child2)
					{
						$widget->add_node($child2);
					}
					$xul_row->add_node($widget);
				}
				$xul_rows->add_node($xul_row);
			}
			$xul_grid->add_node($xul_columns);
			$xul_grid->add_node($xul_rows);

			if ($etempl->style != '')
			{
				$styles = new xmlnode('styles');
				$styles->set_text($etempl->style);
				$xul_grid->add_node($styles);
			}
			$xul_overlay->add_node($xul_grid);

			$doc->add_root($xul_overlay);
			$xml = $doc->dump_mem();

			//if ($this->debug)
			{
				echo "<pre>\n" . htmlentities($xml) . "\n</pre>\n";
			}
			return $xml;
		}

		function import(&$etempl,$data)
		{
			if ($this->debug)
			{
				echo "<pre>\n" . htmlentities($data) . "\n</pre><p>\n";
			}
			$parser = xml_parser_create();
			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
			xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE,   1);
			$vals = $index = '';
			xml_parse_into_struct($parser, $data, $vals, $index);

			if (!is_array($vals))
			{
				$err = xml_error_string(xml_get_error_code($parser));
			}
			xml_parser_free($parser);

			if ($err != '')
			{
				return $err;
			}
			while (list($n,$node) = each($vals))
			{
				$type = $node['type'];
				$tag = $node['tag'];
				$attr = is_array($node['attributes']) ? $node['attributes'] : array();
				if ($attr['id'])
				{
					$attr['name'] = $attr['id']; unset($attr['id']);
				}
				if (isset($attr['options']) && $attr['options'] != '')
				{
					$attr['size'] = $attr['options']; unset($attr['options']);
				}
				if ($tag == 'grid' && $type == 'complete' && !is_array($tab_attr))
				{
					$tag = 'template';
				}
				if ($tag != 'textbox' && !isset($attr['type']))
				{
					$attr['type'] = $this->xul2widget[$tag] ? $this->xul2widget[$tag] : $tag;
				}
				if ($this->debug)
				{
					echo "<p>$node[level]: $tag/$type: value='$node[value]' attr="; _debug_array($attr);
				}
				switch ($tag)
				{
					case 'grid':
						if ($type != 'close' && is_array($tab_attr))
						{
							$tab_names[] = $attr['name'];
							break;
						}
						if ($node['level'] > 2)	// level 1 is the overlay
						{
							return "Can't import nested $node[tag]'s !!!";
						}
						if ($type != 'open')
						{
							break;
						}
						$etempl->init($attr);
						$size_opts = array('padding','spacing','class','border','height','width');
						for ($size = ''; list(,$opt) = each($size_opts); )
						{
							$size = $attr[$opt] . ($size != '' ? ",$size" : '');
						}
						$etempl->size = $size;
						$etempl->cols = $etempl->rows = 0;
						$etempl->data = array();
						break;
					case 'columns':
					case 'rows':
						break;
					case 'column':
						if ($type != 'complete')
						{
							return 'place widgets in <row> and not in <column> !!!';
						}
						$etempl->data[0][$etempl->num2chrs($etempl->cols++)] = $attr['width'];
						break;
					case 'row':
						if ($type != 'open')
						{
							break;
						}
						$r = ++$etempl->rows;
						$col = 0;
						$etempl->data[0]["c$r"] = $attr['class'] . ($attr['valign'] ? ','.$attr['valign'] : '');
						$etempl->data[0]["h$r"] = $attr['height'];
						break;
					case 'styles':
						$etempl->style = trim($node['value']);
						break;
					case 'tabbox':
						if ($type == 'open')
						{
							$tab_labels = $tab_helps = $tab_names = array();
							$tab_attr = $attr;
						}
						else
						{
							$tab_attr['type'] = 'tab';
							$tab_attr['label'] = implode('|',$tab_labels);
							$tab_attr['name'] = implode('|',$tab_names);
							$tab_attr['help'] = implode('|',$tab_helps);
							$spanned = $tab_attr['span'] == 'all' ? $etempl->cols - $col : $tab_attr['span'];
							$tab_attr['span'] .= $tab_attr['class'] ? ','.$tab_attr['class'] : '';
							unset($tab_attr['class']);
							$etempl->data[$etempl->rows][$etempl->num2chrs($col++)] = $tab_attr;
							unset($tab_attr);

							while (--$spanned > 0)
							{
								$etempl->data[$etempl->rows][$etempl->num2chrs($col++)] = $etempl->empty_cell();
							}
						}
						break;
					case 'tabs':
					case 'tabpanels':
						break;
					case 'tab':
						if ($type != 'close')
						{
							$tab_labels[] = $attr['label'];
							$tab_helps[]  = $attr['statustext'];
						}
						break;
					case 'menulist':
						$menulist_attr = $attr;	// save for following menupopup
						break;
					case 'textbox':
						if ($attr['multiline'])
						{
							$attr['type'] = 'textarea';
							$attr['size'] = $attr['cols'] . ($attr['rows'] ? ','.$attr['rows'] : '');
							unset($attr['cols']);
							unset($attr['rows']);
						}
						elseif ($attr['type'])	// integer,float
						{
							$attr['size'] = $attr['min'] . ($attr['max'] ? ','.$attr['max'] : ($attr['size'] ? ',':'')) . ','.$attr['size'];
							unset($attr['min']);
							unset($attr['max']);
						}
						else	// input
						{
							$attr['type'] = 'text';
							$attr['size'] .= $attr['maxlength']!='' ? ','.$attr['maxlength'] : '';
							unset($attr['maxlength']);
						}
						// fall-through
					default:
						switch ($tag)
						{
							case 'description':
							case 'label':
								$attr['label'] = $attr['value'];
								unset($attr['value']);
								break;
							case 'template':
								$attr['size'] = $attr['content'];
								unset($attr['content']);
								break;
							case 'image':
								$attr['label'] = $attr['src'];
								unset($attr['src']);
								break;
							case 'listbox':
								$attr['size'] = $attr['rows'];
								unset($attr['rows']);
								break;
							case 'menupopup':
								if (is_array($menulist_attr))
								{
									$attr += $menulist_attr;
								}
								break;
							case 'button':
								if ($attr['image'] || $attr['ro_image'])
								{
									$attr['size'] = $attr['image'] . ($attr['ro_image'] ? ','.$attr['ro_image'] : '');
									unset($attr['image']); unset($attr['ro_image']);
								}
								break;
						}
						$attr['help'] = $attr['statustext']; unset($attr['statustext']);
						$spanned = $attr['span'] == 'all' ? $etempl->cols - $col : $attr['span'];
						$attr['span'] .= $attr['class'] ? ','.$attr['class'] : ''; unset($attr['class']);
						if ($type == 'close')
						{
							break;
						}
						$etempl->data[$etempl->rows][$etempl->num2chrs($col++)] = $attr;

						while (--$spanned > 0)
						{
							$etempl->data[$etempl->rows][$etempl->num2chrs($col++)] = $etempl->empty_cell();
						}
						break;
				}
			}
			if ($this->debug)
			{
				_debug_array($etempl->data);
			}
			return '';
		}
	}

?>