|
From: <net...@us...> - 2013-02-05 21:09:04
|
Revision: 1430
http://openautomation.svn.sourceforge.net/openautomation/?rev=1430&view=rev
Author: netzkind
Date: 2013-02-05 21:08:56 +0000 (Tue, 05 Feb 2013)
Log Message:
-----------
hopefully fixed problem with multi-dimensional bounds (http://knx-user-forum.de/289905-post256.html); remember the complex-setting across reloads via cookie; pre-check config before loading; allow to appinfo 'descriptor:' in order to have a description for any element; bugfix for adding required elements to a newly created element
Modified Paths:
--------------
CometVisu/trunk/visu/editor/lib/Editor.js
CometVisu/trunk/visu/editor/lib/Schema.js
CometVisu/trunk/visu/upgrade/ConfigurationUpgrader.class.php
CometVisu/trunk/visu/upgrade/index.php
CometVisu/trunk/visu/visu_config.xml
CometVisu/trunk/visu/visu_config.xsd
Added Paths:
-----------
CometVisu/trunk/visu/editor/dependencies/jquery.cookie.js
CometVisu/trunk/visu/editor/editor.html
CometVisu/trunk/visu/editor/index.php
CometVisu/trunk/visu/lib/library_version.inc.php
Removed Paths:
-------------
CometVisu/trunk/visu/editor/index.html
Property Changed:
----------------
CometVisu/trunk/visu/
Property changes on: CometVisu/trunk/visu
___________________________________________________________________
Modified: svn:ignore
- visu_config.xml
visu_config_previewtemp.xml
+ visu_config.xml
Added: CometVisu/trunk/visu/editor/dependencies/jquery.cookie.js
===================================================================
--- CometVisu/trunk/visu/editor/dependencies/jquery.cookie.js (rev 0)
+++ CometVisu/trunk/visu/editor/dependencies/jquery.cookie.js 2013-02-05 21:08:56 UTC (rev 1430)
@@ -0,0 +1,92 @@
+/*!
+ * jQuery Cookie Plugin v1.3.1
+ * https://github.com/carhartl/jquery-cookie
+ *
+ * Copyright 2013 Klaus Hartl
+ * Released under the MIT license
+ */
+(function (factory) {
+ if (typeof define === 'function' && define.amd && define.amd.jQuery) {
+ // AMD. Register as anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals.
+ factory(jQuery);
+ }
+}(function ($) {
+
+ var pluses = /\+/g;
+
+ function raw(s) {
+ return s;
+ }
+
+ function decoded(s) {
+ return decodeURIComponent(s.replace(pluses, ' '));
+ }
+
+ function converted(s) {
+ if (s.indexOf('"') === 0) {
+ // This is a quoted cookie as according to RFC2068, unescape
+ s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
+ }
+ try {
+ return config.json ? JSON.parse(s) : s;
+ } catch(er) {}
+ }
+
+ var config = $.cookie = function (key, value, options) {
+
+ // write
+ if (value !== undefined) {
+ options = $.extend({}, config.defaults, options);
+
+ if (typeof options.expires === 'number') {
+ var days = options.expires, t = options.expires = new Date();
+ t.setDate(t.getDate() + days);
+ }
+
+ value = config.json ? JSON.stringify(value) : String(value);
+
+ return (document.cookie = [
+ encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
+ options.path ? '; path=' + options.path : '',
+ options.domain ? '; domain=' + options.domain : '',
+ options.secure ? '; secure' : ''
+ ].join(''));
+ }
+
+ // read
+ var decode = config.raw ? raw : decoded;
+ var cookies = document.cookie.split('; ');
+ var result = key ? undefined : {};
+ for (var i = 0, l = cookies.length; i < l; i++) {
+ var parts = cookies[i].split('=');
+ var name = decode(parts.shift());
+ var cookie = decode(parts.join('='));
+
+ if (key && key === name) {
+ result = converted(cookie);
+ break;
+ }
+
+ if (!key) {
+ result[name] = converted(cookie);
+ }
+ }
+
+ return result;
+ };
+
+ config.defaults = {};
+
+ $.removeCookie = function (key, options) {
+ if ($.cookie(key) !== undefined) {
+ $.cookie(key, '', $.extend(options, { expires: -1 }));
+ return true;
+ }
+ return false;
+ };
+
+}));
Copied: CometVisu/trunk/visu/editor/editor.html (from rev 1352, CometVisu/trunk/visu/editor/index.html)
===================================================================
--- CometVisu/trunk/visu/editor/editor.html (rev 0)
+++ CometVisu/trunk/visu/editor/editor.html 2013-02-05 21:08:56 UTC (rev 1430)
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CometVisu - configuration editor</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <script src="../dependencies/jquery.js" type="text/javascript"></script>
+ <script src="dependencies/json2.js" type="text/javascript"></script>
+ <script src="dependencies/jquery.cookie.js" type="text/javascript"></script>
+ <script src="dataproviders/DataProviderConfig.js" type="text/javascript"></script>
+ <script src="lib/DataProvider.js" type="text/javascript"></script>
+ <script src="lib/Messages.js" type="text/javascript"></script>
+ <script src="lib/Result.js" type="text/javascript"></script>
+ <script src="lib/ListenerEvent.js" type="text/javascript"></script>
+ <script src="lib/Schema.js" type="text/javascript"></script>
+ <script src="lib/Configuration.js" type="text/javascript"></script>
+ <script src="lib/ActiveInput.js" type="text/javascript"></script>
+ <script src="lib/Editor.js" type="text/javascript"></script>
+ <script src="init.js" type="text/javascript"></script>
+
+ <link rel="stylesheet" type="text/css" href="lib/Editor.css" />
+ <base href="../" />
+ </head>
+ <body style="margin: 0; padding: 0;">
+ <h1>Editor</h1>
+ <p>If you keep seeing this text, then there is something wrong with the cable, and/or Javascript is inactive,
+ and/or there is some heavy problem with this editor. Anyways, please report (and tell us which browser you are
+ using).</p>
+
+ </body>
+</html>
Deleted: CometVisu/trunk/visu/editor/index.html
===================================================================
--- CometVisu/trunk/visu/editor/index.html 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/editor/index.html 2013-02-05 21:08:56 UTC (rev 1430)
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <title>CometVisu - configuration editor</title>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <script src="../dependencies/jquery.js" type="text/javascript"></script>
- <script src="dependencies/json2.js" type="text/javascript"></script>
- <script src="dataproviders/DataProviderConfig.js" type="text/javascript"></script>
- <script src="lib/DataProvider.js" type="text/javascript"></script>
- <script src="lib/Messages.js" type="text/javascript"></script>
- <script src="lib/Result.js" type="text/javascript"></script>
- <script src="lib/ListenerEvent.js" type="text/javascript"></script>
- <script src="lib/Schema.js" type="text/javascript"></script>
- <script src="lib/Configuration.js" type="text/javascript"></script>
- <script src="lib/ActiveInput.js" type="text/javascript"></script>
- <script src="lib/Editor.js" type="text/javascript"></script>
- <script src="init.js" type="text/javascript"></script>
-
- <link rel="stylesheet" type="text/css" href="lib/Editor.css" />
- <base href="../" />
- </head>
- <body style="margin: 0; padding: 0;">
- <h1>Editor</h1>
- <p>If you keep seeing this text, then there is something wrong with the cable, and/or Javascript is inactive,
- and/or there is some heavy problem with this editor. Anyways, please report (and tell us which browser you are
- using).</p>
-
- </body>
-</html>
Added: CometVisu/trunk/visu/editor/index.php
===================================================================
--- CometVisu/trunk/visu/editor/index.php (rev 0)
+++ CometVisu/trunk/visu/editor/index.php 2013-02-05 21:08:56 UTC (rev 1430)
@@ -0,0 +1,122 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Pre-Check the configuration using the embedded schema.
+ * This should make sure that we do not confront the user with javascript-errors
+ * because of an invalid configuration.
+ *
+ *
+ * LICENSE: 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 3
+ * 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, see <http://opensource.org/licenses/gpl-license.php>;.
+ *
+ * @category editor
+ * @package CometVisu
+ * @author Julian Makowski (julian at makowskis dot de)
+ * @copyright 2013 Julian Makowski
+ * @license GPLv3 or later, http://opensource.org/licenses/gpl-license.php
+ * @version SVN: $Id$
+ * @link http://cometvisu.de
+ * @since 2013-02-05
+ */
+
+require_once('../lib/lib_version.inc.php');
+
+define('CONFIG_FILENAME', '../visu_config%s.xml');
+define('SCHEMA_FILENAME', '../%s');
+
+// get everything the user has provided ...
+$strConfigSuffix = (true === isset($_GET['config'])) ? $_GET['config'] : null;
+
+// clean-up filename, we want no security-holes. work with a whitelist.
+$strConfigCleaned = preg_replace("/[^\-\_0-9a-z]/i", "", $strConfigSuffix);
+
+if (false === empty($strConfigCleaned)) {
+ // prefix the postfix with an underscore
+ $strConfigCleaned = '_' . $strConfigCleaned;
+}
+
+// generate the configurations filename
+$strConfigFilename = sprintf(CONFIG_FILENAME, $strConfigCleaned);
+// .. as a fully qualified filename
+$strConfigFQFilename = realpath($strConfigFilename);
+
+if (false === is_writeable($strConfigFQFilename)) {
+ exitWithError('config-file is not writeable by webserver-process; please chmod/chown config-file \'' . $strConfigFQFilename . '\' (\'' . $strConfigFilename. '\').');
+}
+
+// load the configuration
+$objDOM = new DOMDocument("1.0", "UTF-8");
+$objDOM->load($strConfigFQFilename);
+
+// get the configurations schema.
+$strSchemaFile = $objDOM->getElementsByTagName('pages')->item(0)->getAttribute('xsi:noNamespaceSchemaLocation');
+
+if (true === empty($strSchemaFile)) {
+ exitWithError('config-file has no schema associated; do not worry, you probably only need to upgrade its ' .
+ 'structure. This can be done automatically. ' .
+ '<a href="../upgrade/index.php?config=' . $strConfigSuffix . '">Click here to upgrade</a>');
+}
+
+$intConfigurationVersion = $objDOM->getElementsByTagName('pages')->item(0)->getAttribute('lib_version');
+if ($intConfigurationVersion < LIBRARY_VERSION) {
+ exitWithError('Your configuration is not suited for the current version of the library/editor; ' .
+ ' do not worry, you only need to upgrade its structure. This can be done automatically. ' .
+ '<a href="../upgrade/index.php?config=' . $strConfigSuffix . '">Click here to upgrade</a>');
+}
+
+$strSchemaFilename = sprintf(SCHEMA_FILENAME, $strSchemaFile);
+// .. as a fully qualified filename
+$strSchemaFQFilename = realpath($strSchemaFilename);
+
+// disable output of validator
+ob_start();
+// validate the configuration
+$boolValid = $objDOM->schemaValidate($strSchemaFQFilename);
+
+// and flush the validators output
+ob_end_clean();
+
+
+if (true === $boolValid) {
+ // everything is good
+ header('Location: editor.html?config=' . $strConfigSuffix);
+ exit;
+} else {
+ // not everything is good, have check_config look at it.
+ header('Location: ../check_config.php?config=' . $strConfigSuffix);
+ exit;
+}
+
+/**
+ * create and send a response.
+ * This function will issue an exit command!
+ *
+ * @param string $strMessage a message, if any
+ */
+function exitWithError($strMessage = '') {
+
+ header('Content-type: text/html');
+ print '<html><head>';
+ print '<title>configuration pre-check</title>';
+ print '</head><body>';
+
+ print '<b style="color: red;">ERROR</b>: ';
+ print $strMessage;
+
+ print '</body></html>';
+
+ exit;
+}
+
+?>
\ No newline at end of file
Modified: CometVisu/trunk/visu/editor/lib/Editor.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Editor.js 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/editor/lib/Editor.js 2013-02-05 21:08:56 UTC (rev 1430)
@@ -70,6 +70,11 @@
*/
var isExpert = false;
+ if ($.cookie('editor_complex') == true || $.cookie('editor_complex') == 'true') {
+ // read the expert-cookie
+ isExpert = true;
+ }
+
var clickHandler = function (event) {
var $button = $(this);
@@ -116,6 +121,9 @@
$button.toggleClass('active');
isExpert = $button.hasClass('active');
+
+ // save the current state to the cookie
+ $.cookie('editor_complex', isExpert, {expires: 365});
}
};
@@ -218,6 +226,10 @@
.attr('title', Messages.editor.ui.expert.tooltip)
.click(clickHandler);
$menu.append($expert);
+ if (isExpert == true) {
+ // add the active-class even at startup, it might be pre-set
+ $expert.addClass('active');
+ }
var $preview = $('<span />')
.addClass('button')
@@ -1733,9 +1745,30 @@
var $name = $('<span />').addClass('name').html(_element.name).addClass('nodeType_' + _element.name);
var $nameValue = $('<span />').addClass('nameValue');
- if (typeof _element.attributes.name != 'undefined' && _element.attributes.name.trim() != '') {
- $nameValue.text(_element.attributes.name);
- $nameValue.addClass('set');
+ // find out which fields functions as descriptor for this element, if any
+ var properties = _element.getSchemaElement().getAppinfo();
+
+ var descriptor = 'name'; // default to the field 'name' as descriptor
+ $.each(properties, function (i, value) {
+ if (/^descriptor:/.test(value)) {
+ descriptor = value.replace(/^descriptor:/, '');
+ }
+ });
+ delete properties;
+
+ if (descriptor == '#text') {
+ // we need to get the text-content as descriptor. do not abuse this, or you might get punched
+ // in the face by adding markup to the editor at places you did not expect
+ if (_element.value != undefined && _element.value.trim() != '') {
+ $nameValue.text(_element.value);
+ $nameValue.addClass('set');
+ }
+ } else {
+ // its an attribute
+ if (typeof _element.attributes[descriptor] != 'undefined' && _element.attributes[descriptor].trim() != '') {
+ $nameValue.text(_element.attributes[descriptor]);
+ $nameValue.addClass('set');
+ }
}
$name.append($nameValue);
delete $nameValue;
Modified: CometVisu/trunk/visu/editor/lib/Schema.js
===================================================================
--- CometVisu/trunk/visu/editor/lib/Schema.js 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/editor/lib/Schema.js 2013-02-05 21:08:56 UTC (rev 1430)
@@ -796,6 +796,8 @@
* @return boolean are children sortable?
*/
_element.areChildrenSortable = function () {
+ var allowedContent = _element.getAllowedContent();
+
if (allowedContent._grouping == undefined) {
return undefined;
}
@@ -812,6 +814,8 @@
* @return array list of required elements
*/
_element.getRequiredElements = function () {
+ var allowedContent = _element.getAllowedContent();
+
if (allowedContent._grouping != undefined) {
// we do have a grouping as a child
return allowedContent._grouping.getRequiredElements();
@@ -849,6 +853,8 @@
* @return object list of allowed elements, with their sort-number as value
*/
_element.getAllowedElementsSorting = function () {
+ var allowedContent = _element.getAllowedContent();
+
if (allowedContent._grouping != undefined) {
return allowedContent._grouping.getAllowedElementsSorting();
}
@@ -862,6 +868,8 @@
* @return object bounds ({min: x, max: y})
*/
_element.getChildBounds = function () {
+ var allowedContent = _element.getAllowedContent();
+
if (allowedContent._grouping == undefined) {
// no choice = no idea about bounds
return undefined;
@@ -883,6 +891,8 @@
* @return object {min: x, max: y}
*/
_element.getBoundsForElementName = function (childName) {
+ var allowedContent = _element.getAllowedContent();
+
return allowedContent._grouping.getBoundsForElementName(childName);
};
@@ -1130,6 +1140,8 @@
return true;
}
+ var allowedContent = _element.getAllowedContent();
+
return allowedContent._text.isValueValid(value);
}
@@ -1187,6 +1199,8 @@
separator = '';
}
+ var allowedContent = _element.getAllowedContent();
+
if (allowedContent._grouping == undefined) {
// not really something to match
return '^';
@@ -1558,29 +1572,37 @@
* @return object {max: x, min: y}, or undefined if none found
*/
_choice.getBoundsForElementName = function (childName) {
- // if this element is our immediate child, we have to say about bounds
- if (typeof allowedElements[childName] !== 'undefined') {
+ // as we are a choice, we can define the number of occurences for children of ANY level
+ if (true === _choice.isElementAllowed(childName)) {
return _choice.getBounds();
}
-
- // though we are a child-element, our sub-grouping might have another saying than we do about the bounds
- var childBounds = undefined;
- $.each(subGroupings, function (i, subGroup) {
- if (typeof childBounds != 'undefined') {
- // do not look further if we already have bounds
- return;
- }
-
- // if the subGroup allows this element, we get the bounds from there
- if (true === subGroup.isElementAllowed(childName)) {
- childBounds = subGroup.getBoundsForElementName(childName);
- }
-
- });
-
-
- return childBounds;
+ return undefined;
+//
+// // if this element is our immediate child, we have to say about bounds
+// if (typeof allowedElements[childName] !== 'undefined') {
+// return _choice.getBounds();
+// }
+//
+// // though we are a child-element, our sub-grouping might have another saying than we do about the bounds
+// var childBounds = undefined;
+//
+// $.each(subGroupings, function (i, subGroup) {
+// if (typeof childBounds != 'undefined') {
+// // do not look further if we already have bounds
+// return;
+// }
+//
+// // if the subGroup allows this element, we still use our own bounds, as they are superior, and we still
+// // are a choice!
+// if (true === subGroup.isElementAllowed(childName)) {
+// childBounds = _choice.getBounds();
+// }
+//
+// });
+//
+//
+// return childBounds;
};
/**
Added: CometVisu/trunk/visu/lib/library_version.inc.php
===================================================================
--- CometVisu/trunk/visu/lib/library_version.inc.php (rev 0)
+++ CometVisu/trunk/visu/lib/library_version.inc.php 2013-02-05 21:08:56 UTC (rev 1430)
@@ -0,0 +1,37 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * define the current version of the library
+ *
+ *
+ * LICENSE: 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 3
+ * 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, see <http://opensource.org/licenses/gpl-license.php>;.
+ *
+ * @category visu
+ * @package CometVisu
+ * @author Julian Makowski (julian at makowskis dot de)
+ * @copyright 2013 Julian Makowski
+ * @license GPLv3 or later, http://opensource.org/licenses/gpl-license.php
+ * @version SVN: $Id$
+ * @link http://cometvisu.de
+ * @since 2013-02-05
+ */
+
+/**
+ * the current lib version
+ * @const integer
+ */
+define('LIBRARY_VERSION', 1);
+
+?>
Modified: CometVisu/trunk/visu/upgrade/ConfigurationUpgrader.class.php
===================================================================
--- CometVisu/trunk/visu/upgrade/ConfigurationUpgrader.class.php 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/upgrade/ConfigurationUpgrader.class.php 2013-02-05 21:08:56 UTC (rev 1430)
@@ -28,11 +28,14 @@
* @since 2013-01-22
*/
+// get the current library version
+require_once('../lib/lib_version.inc.php');
+
/**
- * the current lib version
+ * the library-version the upgrader understands
* @const integer
*/
-define('LIBRARY_VERSION', 1);
+define('UPGRADER_LIBRARY_VERSION', 1);
/**
Modified: CometVisu/trunk/visu/upgrade/index.php
===================================================================
--- CometVisu/trunk/visu/upgrade/index.php 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/upgrade/index.php 2013-02-05 21:08:56 UTC (rev 1430)
@@ -34,6 +34,15 @@
require_once('ConfigurationUpgrader.class.php');
+if (LIBRARY_VERSION > UPGRADER_LIBRARY_VERSION) {
+ // the library moved faster than the upgrader
+ exitWithResponse(false, 'The upgrader itself is not up-to-date. ' .
+ 'Library is at version ' . LIBRARY_VERSION . ', ' .
+ 'upgrader at version ' . UPGRADER_LIBRARY_VERSION . '. ' .
+ 'Please report this problem at http://www.knx-user-forum.de/cometvisu/');
+}
+
+
define('CONFIG_FILENAME', '../visu_config%s.xml');
define('BACKUP_FILENAME', '../backup/visu_config%s-%s.xml');
@@ -132,7 +141,7 @@
* @param string $strMessage a message, if any
*/
function exitWithResponse($boolSuccess, $strMessage = '') {
- if ($boolSuccess == true) {
+ if (true === $boolSuccess) {
print '<b style="color: green;">SUCCESS</b>';
} else {
print '<b style="color: red;">ERROR</b>';
@@ -143,6 +152,14 @@
print $strMessage;
}
+ if (true === $boolSuccess) {
+ // despite globals being bad in general, this is way easier.
+ global $strConfigSuffix;
+
+ print '<p><a href="../editor/?config=' . $strConfigSuffix . '">open config in editor</a></p>';
+ print '<p><a href="../?config=' . $strConfigSuffix . '">show in CometVisu</a></p>';
+ }
+
outputFooter();
exit;
Modified: CometVisu/trunk/visu/visu_config.xml
===================================================================
--- CometVisu/trunk/visu/visu_config.xml 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/visu_config.xml 2013-02-05 21:08:56 UTC (rev 1430)
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
-<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" design="pure" xsi:noNamespaceSchemaLocation="./visu_config.xsd" lib_version="0">
+<pages xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" design="pure" lib_version="0" xsi:noNamespaceSchemaLocation="./visu_config.xsd">
<meta>
<plugins>
<plugin name="colorchooser"/>
Modified: CometVisu/trunk/visu/visu_config.xsd
===================================================================
--- CometVisu/trunk/visu/visu_config.xsd 2013-02-05 21:00:54 UTC (rev 1429)
+++ CometVisu/trunk/visu/visu_config.xsd 2013-02-05 21:08:56 UTC (rev 1430)
@@ -37,6 +37,9 @@
</xsd:complexType>
<xsd:complexType name="entry" mixed="true">
+ <xsd:annotation>
+ <xsd:appinfo>descriptor:#text</xsd:appinfo>
+ </xsd:annotation>
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="icon" type="icon" />
</xsd:choice>
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|