diff --git a/src/requirements.txt b/src/requirements.txt
index 60a244ccd6..61c6e82826 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -15,3 +15,4 @@ pyScss>=1.3.4,<2.0
Pygments
pillow
jsonpatch
+jsonschema
\ No newline at end of file
diff --git a/src/wirecloud/catalogue/fixtures/catalogue_search_data.json b/src/wirecloud/catalogue/fixtures/catalogue_search_data.json
index 60d7a9799f..45e83e46cf 100644
--- a/src/wirecloud/catalogue/fixtures/catalogue_search_data.json
+++ b/src/wirecloud/catalogue/fixtures/catalogue_search_data.json
@@ -77,7 +77,7 @@
"popularity": "0",
"vendor": "Wirecloud",
"short_name": "Test",
- "json_description": "{\"name\": \"Test\", \"vendor\": \"Wirecloud\", \"version\": \"1.5\", \"type\": \"widget\", \"title\": \"Incredible Test\", \"description\": \"This widget contains keywords like term mashable application component.\", \"wiring\": {\"inputs\": [{\"friendcode\": \"test-data\", \"actionlabel\": \"\", \"name\": \"inputendpoint\", \"label\": \"Input\", \"type\": \"text\", \"description\": \"input type text\"}], \"outputs\": [{\"friendcode\": \"test-data\", \"description\": \"output digit\", \"type\": \"text\", \"name\": \"outputendpoint\", \"label\": \"Output\"}]}, \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Test\", \"vendor\": \"Wirecloud\", \"version\": \"1.5\", \"type\": \"widget\", \"title\": \"Incredible Test\", \"description\": \"This widget contains keywords like term mashable application component.\", \"wiring\": {\"inputs\": [{\"friendcode\": \"test-data\", \"actionlabel\": \"\", \"name\": \"inputendpoint\", \"label\": \"Input\", \"type\": \"text\", \"description\": \"input type text\"}], \"outputs\": [{\"friendcode\": \"test-data\", \"description\": \"output digit\", \"type\": \"text\", \"name\": \"outputendpoint\", \"label\": \"Output\"}]}, \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-12T11:24:03Z",
"version": "1.5",
@@ -95,7 +95,7 @@
"popularity": "0",
"vendor": "Wirecloud",
"short_name": "Test",
- "json_description": "{\"name\": \"Test\", \"vendor\": \"Wirecloud\", \"version\": \"2.0\", \"type\": \"widget\", \"title\": \"New Incredible Test\", \"description\": \"This widget contains keywords like prefix mashable application component.\", \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Test\", \"vendor\": \"Wirecloud\", \"version\": \"2.0\", \"type\": \"widget\", \"title\": \"New Incredible Test\", \"description\": \"This widget contains keywords like prefix mashable application component.\", \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-13T11:24:03Z",
"version": "2.0",
@@ -113,7 +113,7 @@
"popularity": "0",
"vendor": "Wirecloud",
"short_name": "Test",
- "json_description": "{\"name\": \"Test\", \"vendor\": \"Wirecloud\", \"version\": \"2.5\", \"type\": \"widget\", \"title\": \"The Best Incredible Test\", \"description\": \"This widget contains keywords regex mashable application component.\", \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Test\", \"vendor\": \"Wirecloud\", \"version\": \"2.5\", \"type\": \"widget\", \"title\": \"The Best Incredible Test\", \"description\": \"This widget contains keywords regex mashable application component.\", \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-14T11:24:03Z",
"version": "2.5",
@@ -131,7 +131,7 @@
"popularity": "0",
"vendor": "CoNWeT-Lab",
"short_name": "Clock_Now",
- "json_description": "{\"name\": \"Clock_Now\", \"vendor\": \"CoNWeT-Lab\", \"version\": \"1.5\", \"type\": \"widget\", \"title\": \"Clock Now\", \"description\": \"This WireCloud widget contains keywords like free button bluetooth.\", \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Clock_Now\", \"vendor\": \"CoNWeT-Lab\", \"version\": \"1.5\", \"type\": \"widget\", \"title\": \"Clock Now\", \"description\": \"This WireCloud widget contains keywords like free button bluetooth.\", \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-15T11:24:03Z",
"version": "1.5",
@@ -149,7 +149,7 @@
"popularity": "0",
"vendor": "CoNWeT-Lab",
"short_name": "Clock_Now",
- "json_description": "{\"name\": \"Clock_Now\", \"vendor\": \"CoNWeT-Lab\", \"version\": \"1.11\", \"type\": \"widget\", \"title\": \"Last Clock Now\", \"description\": \"This WireCloud widget contains keywords like free button bluetooth.\", \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Clock_Now\", \"vendor\": \"CoNWeT-Lab\", \"version\": \"1.11\", \"type\": \"widget\", \"title\": \"Last Clock Now\", \"description\": \"This WireCloud widget contains keywords like free button bluetooth.\", \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-16T11:24:03Z",
"version": "1.11",
@@ -347,7 +347,7 @@
"popularity": "0",
"vendor": "Wirecloud",
"short_name": "Book-Reader",
- "json_description": "{\"name\": \"Book-Reader\", \"vendor\": \"Wirecloud\", \"version\": \"1.5\", \"type\": \"widget\", \"title\": \"The Best Incredible Test\", \"description\": \"This widget contains keywords wiredrawers wiretappers wirephoto wirework wireway wired wireless.\", \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Book-Reader\", \"vendor\": \"Wirecloud\", \"version\": \"1.5\", \"type\": \"widget\", \"title\": \"The Best Incredible Test\", \"description\": \"This widget contains keywords wiredrawers wiretappers wirephoto wirework wireway wired wireless.\", \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-27T11:24:03Z",
"version": "1.5",
@@ -365,7 +365,7 @@
"popularity": "0",
"vendor": "CoNWeT-Lab",
"short_name": "Live_Sports",
- "json_description": "{\"name\": \"Live_Sports\", \"vendor\": \"CoNWeT-Lab\", \"version\": \"1.0\", \"type\": \"widget\", \"title\": \"Live Sports\", \"description\": \"This widget contains keywords like free button bluetooth.\", \"contents\": {\"src\": \"test.html\"}}",
+ "json_description": "{\"name\": \"Live_Sports\", \"vendor\": \"CoNWeT-Lab\", \"version\": \"1.0\", \"type\": \"widget\", \"title\": \"Live Sports\", \"description\": \"This widget contains keywords like free button bluetooth.\", \"contents\": {\"src\": \"test.html\"}, \"widget_width\": \"4\", \"widget_height\": \"4\"}",
"creator": 80,
"creation_date": "2011-05-28T11:24:03Z",
"version": "1.0",
diff --git a/src/wirecloud/commons/utils/template/parsers/json.py b/src/wirecloud/commons/utils/template/parsers/json.py
index 0c1fe5af64..7052687180 100644
--- a/src/wirecloud/commons/utils/template/parsers/json.py
+++ b/src/wirecloud/commons/utils/template/parsers/json.py
@@ -19,6 +19,8 @@
# along with Wirecloud. If not, see .
import json
+from jsonschema.validators import Draft7Validator
+import os
from django.utils.translation import ugettext as _
@@ -26,6 +28,9 @@
from wirecloud.commons.utils.translation import get_trans_index
from wirecloud.platform.wiring.utils import get_wiring_skeleton, parse_wiring_old_version
+with open(os.path.join(os.path.dirname(__file__), '../schemas/json_schema.schema.json'), 'r') as JSONSCHEMA_FILE:
+ JSONSCHEMA = json.load(JSONSCHEMA_FILE)
+ validator = Draft7Validator(JSONSCHEMA)
class JSONTemplateParser(object):
@@ -189,7 +194,7 @@ def _add_translation_index(self, value, **kwargs):
self._info['translation_index_usage'][index].append(kwargs)
def _init(self):
-
+ validator.validate(self._info, JSONSCHEMA)
self._check_string_fields(('title', 'description', 'longdescription', 'email', 'homepage', 'doc', 'changelog', 'image', 'smartphoneimage', 'license', 'licenseurl', 'issuetracker'))
self._check_contacts_fields(('authors', 'contributors'))
self._check_integer_fields(('macversion', ), default = 1)
diff --git a/src/wirecloud/commons/utils/template/schemas/json_schema.schema.json b/src/wirecloud/commons/utils/template/schemas/json_schema.schema.json
new file mode 100644
index 0000000000..bc698caa89
--- /dev/null
+++ b/src/wirecloud/commons/utils/template/schemas/json_schema.schema.json
@@ -0,0 +1,1414 @@
+{
+ "$id": "https://raw.githubusercontent.com/Wirecloud/wirecloud/develop/src/wirecloud/commons/utils/template/schemas/json_schema.schema.json",
+ "$schema": "https://json-schema.org/draft-07/schema",
+ "title": "WireCloud Mashable Application Component Description",
+ "type": "object",
+ "required": [
+ "vendor",
+ "name",
+ "version"
+ ],
+ "properties": {
+ "authors": {
+ "description": "Main developers of this mashable application component.",
+ "allOf": [
+ {
+ "$ref": "#/$defs/contacts"
+ }
+ ]
+ },
+ "changelog": {
+ "description": "Relative path to a markdown file detailing the changes made to the mashable application component in each version.",
+ "type": "string"
+ },
+ "contributors": {
+ "description": "Contributors of this mashable application component.",
+ "allOf": [
+ {
+ "$ref": "#/$defs/contacts"
+ }
+ ]
+ },
+ "description": {
+ "description": "A brief textual description of the mashable application component.",
+ "type": "string"
+ },
+ "doc": {
+ "description": "Absolute or template-relative URL of the widget documentation.",
+ "type": "string"
+ },
+ "email": {
+ "description": "E-mail address to get in touch with the developer(s).",
+ "type": "string",
+ "format": "email"
+ },
+ "homepage": {
+ "description": "The url to the project homepage.",
+ "type": "string"
+ },
+ "image": {
+ "description": "Absolute or template-relative URL of the resource image for the catalog.",
+ "type": "string"
+ },
+ "issuetracker": {
+ "description": "Absolute URL of the issue tracker for the mashable application component.",
+ "type": "string"
+ },
+ "license": {
+ "description": "Name of the license associated to the mashable application component.",
+ "type": "string",
+ "examples": [
+ "Apache 2.0",
+ "MIT",
+ "GPLv3",
+ "BSD"
+ ]
+ },
+ "licenseurl": {
+ "description": "Absolute or template-relative URL of the full license document associated to the mashable application component.",
+ "type": "string"
+ },
+ "longdescription": {
+ "description": "A detailed description of the mashable application component (using Markdown syntax by default).",
+ "type": "string"
+ },
+ "macversion": {
+ "description": "Version of the MACD specification used to describe the mashable application component.",
+ "default": 1,
+ "enum": [
+ 1,
+ 2
+ ],
+ "type": "integer"
+ },
+ "name": {
+ "description": "Name of the mashable application component.",
+ "type": "string"
+ },
+ "requirements": {
+ "description": "List of requirements for the mashable application component.",
+ "required": [
+ "name"
+ ],
+ "type": "array",
+ "items": {
+ "properties": {
+ "name": {
+ "description": "Name of the requirement.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of the requirement. Currently only `feature` is supported.",
+ "type": "string",
+ "enum": [
+ "feature"
+ ]
+ }
+ },
+ "additionalProperties": false,
+ "type": "object",
+ "examples": [
+ {
+ "type": "feature",
+ "name": "StyledElements"
+ },
+ {
+ "type": "feature",
+ "name": "DashboardManagement"
+ }
+ ]
+ }
+ },
+ "smartphoneimage": {
+ "description": "Absolute or template-relative URL of the resource image for the catalog to be used on phones",
+ "type": "string"
+ },
+ "title": {
+ "default": "",
+ "description": "Name used in the user interface for the widget. This field can be translated, therefore this field is not used to uniquely identify the widget.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of the mashable application component.",
+ "enum": [
+ "mashup",
+ "operator",
+ "widget"
+ ],
+ "type": "string"
+ },
+ "vendor": {
+ "description": "Name of the vendor of the mashable application component.",
+ "type": "string"
+ },
+ "version": {
+ "description": "Version of the mashable application component.",
+ "$ref": "#/$defs/version"
+ },
+ "translations": {
+ "$ref": "#/$defs/translations"
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "enum": [
+ "widget",
+ "operator"
+ ]
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "preferences": {
+ "description": "The user preferences, which may be changed through the platform interface.",
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/component_preference"
+ }
+ },
+ "properties": {
+ "description": "The user properties, which the component code might change. Also known as persistent variables.",
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/component_property"
+ }
+ },
+ "wiring": {
+ "description": "Wiring of the component. It defines the inputs and outputs of the component.",
+ "default": {},
+ "allOf": [
+ {
+ "$ref": "#/$defs/wiring"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "widget"
+ },
+ "macversion": {
+ "const": 1
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "contents": {
+ "description": "The contents of the widget.",
+ "type": "object",
+ "required": [
+ "src"
+ ],
+ "properties": {
+ "src": {
+ "type": "string"
+ },
+ "contenttype": {
+ "type": "string",
+ "default": "text/html"
+ },
+ "charset": {
+ "type": "string",
+ "default": "utf-8"
+ },
+ "useplatformstyle": {
+ "type": "boolean",
+ "default": false
+ },
+ "cacheable": {
+ "type": "boolean",
+ "default": true
+ },
+ "altcontents": {
+ "description": "Alternative contents of the widget",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "src",
+ "scope"
+ ],
+ "properties": {
+ "src": {
+ "type": "string"
+ },
+ "scope": {
+ "type": "string",
+ "enum": [
+ "local",
+ "global"
+ ]
+ },
+ "contenttype": {
+ "type": "string",
+ "default": "text/html"
+ },
+ "charset": {
+ "type": "string",
+ "default": "utf-8"
+ }
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "widget"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "widget_width",
+ "widget_height"
+ ],
+ "properties": {
+ "widget_width": {
+ "description": "Width of the widget.",
+ "$ref": "#/$defs/size"
+ },
+ "widget_height": {
+ "description": "Height of the widget.",
+ "$ref": "#/$defs/size"
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "anyOf": [
+ {
+ "properties": {
+ "type": {
+ "const": "widget"
+ },
+ "macversion": {
+ "minimum": 2
+ }
+ }
+ },
+ {
+ "properties": {
+ "type": {
+ "const": "operator"
+ }
+ }
+ }
+ ]
+ },
+ "then": {
+ "properties": {
+ "js_files": {
+ "description": "List of javascript files the component will use.",
+ "type": "array",
+ "items": {
+ "description": "A javascript file.",
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "mashup"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "preferences": {
+ "description": "The user preferences.",
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "type": "string",
+ "description": "A user preference."
+ }
+ }
+ },
+ "tabs": {
+ "description": "List of mashup tabs.",
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/mashup_tab"
+ }
+ },
+ "params": {
+ "description": "Mashup parameters.",
+ "type": "array",
+ "items": {
+ "$ref": "#/$defs/mashup_param"
+ }
+ },
+ "embedded": {
+ "description": "The list of embedded resources for this mashup.",
+ "type": "array",
+ "required": [
+ "vendor",
+ "name",
+ "version",
+ "src"
+ ],
+ "items": {
+ "type": "object",
+ "properties": {
+ "vendor": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "version": {
+ "$ref": "#/$defs/version"
+ },
+ "src": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "wiring": {
+ "default": {
+ "version": "2.0",
+ "connections": [],
+ "operators": {},
+ "visualdescription": {
+ "behaviours": [],
+ "components": {
+ "operator": {},
+ "widget": {}
+ },
+ "connections": []
+ }
+ },
+ "allOf": [
+ {
+ "$ref": "#/$defs/wiring"
+ },
+ {
+ "$ref": "#/$defs/wiring_mashup"
+ }
+ ],
+ "description": "Mashup wiring."
+ },
+ "screenSizes": {
+ "description": "List of screen sizes supported by the mashup.",
+ "default": [],
+ "items": {
+ "$ref": "#/$defs/mashup_screenSize"
+ },
+ "type": "array"
+ },
+ "layout": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "enum": [
+ "widget",
+ "operator"
+ ]
+ },
+ "macversion": {
+ "minimum": 2
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "entrypoint": {
+ "type": "string",
+ "description": "The entrypoint of the component. (deprecated)"
+ }
+ }
+ }
+ }
+ ],
+ "$defs": {
+ "component_preference": {
+ "description": "The user preferences, which may be changed through the platform interface. This element consisting of one, several or even none preference sub-elements.",
+ "type": "object",
+ "required": [
+ "name",
+ "type"
+ ],
+ "properties": {
+ "name": {
+ "description": "Name of the preference.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of the preference.",
+ "type": "string",
+ "enum": [
+ "text",
+ "boolean",
+ "number",
+ "password",
+ "list",
+ "code"
+ ]
+ },
+ "label": {
+ "description": "Label of the preference.",
+ "type": "string"
+ },
+ "description": {
+ "description": "Description of the preference.",
+ "type": "string"
+ },
+ "default": {
+ "description": "Default value of the preference.",
+ "type": [
+ "string",
+ "boolean",
+ "number"
+ ]
+ },
+ "readOnly": {
+ "description": "Whether the preference is read-only.",
+ "default": false,
+ "type": "boolean"
+ },
+ "secure": {
+ "description": "Direct access from the javascript code of the application mashup component to this variable is disallowed if the value of this attribute is true.",
+ "default": false,
+ "type": "boolean"
+ },
+ "required": {
+ "description": "Whether the preference is required.",
+ "default": false,
+ "type": "boolean"
+ },
+ "multiuser": {
+ "description": "Whether to store a different value for each user.",
+ "default": false,
+ "type": "boolean"
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "list"
+ }
+ }
+ },
+ "then": {
+ "required": [
+ "options"
+ ],
+ "properties": {
+ "options": {
+ "description": "List of options for the preference.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "value": {
+ "description": "Value of the preference.",
+ "type": [
+ "string",
+ "boolean",
+ "number"
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "else": {
+ "description": "The value of the preference.",
+ "properties": {
+ "value": {
+ "type": [
+ "string",
+ "boolean",
+ "number",
+ "null"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "if": {
+ "properties": {
+ "type": {
+ "const": "code"
+ }
+ }
+ },
+ "then": {
+ "properties": {
+ "language": {
+ "description": "Language the code editor should colorize and/or autocomplete for.",
+ "type": "string",
+ "examples": [
+ "javascript",
+ "python"
+ ]
+ }
+ }
+ }
+ }
+ ]
+ },
+ "component_property": {
+ "description": "A property that saves persistent information about the component, also known as a persistent variable.",
+ "type": "object",
+ "required": [
+ "name",
+ "type"
+ ],
+ "properties": {
+ "name": {
+ "description": "Name of the property.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of the property.",
+ "type": "string",
+ "enum": [
+ "text",
+ "boolean",
+ "number",
+ "password",
+ "list"
+ ]
+ },
+ "label": {
+ "description": "Label of the property.",
+ "type": "string"
+ },
+ "description": {
+ "description": "Description of the property.",
+ "type": "string"
+ },
+ "default": {
+ "description": "Default value of the property.",
+ "type": [
+ "string",
+ "boolean",
+ "number"
+ ]
+ },
+ "secure": {
+ "description": "Direct access from the javascript code of the application mashup component to this variable is disallowed if the value of this attribute is true.",
+ "type": "boolean",
+ "default": false
+ },
+ "multiuser": {
+ "description": "Whether to store a different value for each user.",
+ "type": "boolean",
+ "default": false
+ }
+ }
+ },
+ "mashup_screenSize": {
+ "properties": {
+ "id": {
+ "minimum": 0,
+ "type": "integer"
+ },
+ "moreOrEqual": {
+ "minimum": 0,
+ "type": "integer"
+ },
+ "lessOrEqual": {
+ "minimum": -1,
+ "type": "integer"
+ },
+ "rendering": {
+ "$ref": "#/$defs/mashup_rendering"
+ },
+ "position": {
+ "$ref": "#/$defs/mashup_position"
+ }
+ },
+ "required": [
+ "id",
+ "moreOrEqual",
+ "lessOrEqual",
+ "rendering",
+ "position"
+ ],
+ "type": "object"
+ },
+ "mashup_tab": {
+ "description": "A mashup tab.",
+ "type": "object",
+ "required": [
+ "name",
+ "preferences",
+ "resources"
+ ],
+ "properties": {
+ "name": {
+ "type": "string",
+ "description": "Name of the tab."
+ },
+ "title": {
+ "type": "string",
+ "description": "Title of the tab."
+ },
+ "resources": {
+ "description": "List of resources contained in the tab.",
+ "type": "array",
+ "items": {
+ "description": "A resource contained in a tab.",
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "vendor",
+ "version",
+ "title",
+ "position",
+ "rendering"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "vendor": {
+ "type": "string"
+ },
+ "version": {
+ "$ref": "#/$defs/version"
+ },
+ "title": {
+ "type": "string"
+ },
+ "readOnly": {
+ "type": "boolean",
+ "default": false
+ },
+ "properties": {
+ "type": "object",
+ "default": {},
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "readonly": {
+ "type": "boolean",
+ "default": false
+ },
+ "hidden": {
+ "type": "boolean",
+ "default": false
+ },
+ "value": {
+ "type": [
+ "string",
+ "boolean",
+ "number",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "preferences": {
+ "type": "object",
+ "default": {},
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "readonly": {
+ "type": "boolean",
+ "default": false
+ },
+ "value": {
+ "type": [
+ "string",
+ "boolean",
+ "number",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ },
+ "rendering": {
+ "$ref": "#/$defs/mashup_rendering"
+ },
+ "position": {
+ "$ref": "#/$defs/mashup_position"
+ }
+ }
+ }
+ }
+ }
+ },
+ "mashup_param": {
+ "description": "A mashup parameter.",
+ "type": "object",
+ "required": [
+ "name",
+ "type",
+ "value"
+ ],
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "type": {
+ "type": "string"
+ },
+ "label": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "readonly": {
+ "type": "boolean",
+ "default": false
+ },
+ "default": {
+ "type": [
+ "string",
+ "boolean",
+ "number"
+ ]
+ },
+ "value": {
+ "type": [
+ "string",
+ "boolean",
+ "number",
+ "null"
+ ]
+ },
+ "required": {
+ "type": "boolean",
+ "default": false
+ }
+ }
+ },
+ "mashup_rendering": {
+ "type": "object",
+ "required": [
+ "width",
+ "height"
+ ],
+ "properties": {
+ "fulldragboard": {
+ "type": "boolean"
+ },
+ "minimized": {
+ "type": "boolean"
+ },
+ "width": {
+ "$ref": "#/$defs/size"
+ },
+ "height": {
+ "$ref": "#/$defs/size"
+ },
+ "layout": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ },
+ "relwidth": {
+ "type": "boolean"
+ },
+ "relheight": {
+ "type": "boolean"
+ },
+ "titlevisible": {
+ "type": "boolean"
+ }
+ }
+ },
+ "mashup_position": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/Position2D"
+ }
+ ],
+ "type": "object",
+ "required": [
+ "z"
+ ],
+ "properties": {
+ "anchor": {
+ "type": "string"
+ },
+ "relx": {
+ "type": "boolean"
+ },
+ "rely": {
+ "type": "boolean"
+ },
+ "z": {
+ "type": [
+ "number",
+ "string"
+ ]
+ }
+ }
+ },
+ "Position2D": {
+ "type": "object",
+ "required": [
+ "x",
+ "y"
+ ],
+ "properties": {
+ "x": {
+ "type": [
+ "number",
+ "string"
+ ]
+ },
+ "y": {
+ "type": [
+ "number",
+ "string"
+ ]
+ }
+ }
+ },
+ "contact": {
+ "type": [
+ "object",
+ "string"
+ ],
+ "required": [
+ "name"
+ ],
+ "properties": {
+ "name": {
+ "description": "Name of the contact.",
+ "type": "string"
+ },
+ "email": {
+ "description": "Email of the contact.",
+ "type": "string"
+ },
+ "url": {
+ "description": "URL of the contact.",
+ "type": "string"
+ }
+ },
+ "additionalProperties": false,
+ "examples": [
+ {
+ "name": "John Doe",
+ "email": "john-doe@example.com",
+ "url": "http://example.com/johndoe"
+ },
+ "John Doe (http://example.com/johndoe)"
+ ]
+ },
+ "contacts": {
+ "type": [
+ "array",
+ "string"
+ ],
+ "items": {
+ "$ref": "#/$defs/contact"
+ }
+ },
+ "wiring": {
+ "type": "object",
+ "required": [],
+ "properties": {
+ "version": {
+ "type": "string",
+ "default": "1.0"
+ },
+ "inputs": {
+ "description": "List of inputs of the component. This component will be able to receive data from the inputs listed here.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "type"
+ ],
+ "properties": {
+ "name": {
+ "description": "Name that will uniquely identify this input.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of the input. This field can only be set to `text` right now.",
+ "type": "string",
+ "enum": [
+ "text"
+ ]
+ },
+ "label": {
+ "description": "Label of the input. This field is used to show a human-readable and translatable name of the input.",
+ "type": "string"
+ },
+ "description": {
+ "description": "Text describing what is going to happen if an event arrives this input endpoint. This description is very important for the wiring process as the user needs this information for taking decisions on how to wire widgets/operators.",
+ "type": "string"
+ },
+ "actionlabel": {
+ "description": "Short text describing what is going to happen if an event is sent to this input endpoint. Other widgets will use this text in buttons, selection boxes, etc... allowing end users to select what to do (and the widget will send a event to the associated target endpoint).",
+ "type": "string"
+ },
+ "friendcode": {
+ "description": "Keyword used to tag the input endpoint, so it can be easily suggested valid conection during the wiring process.",
+ "type": "string"
+ }
+ },
+ "examples": [
+ {
+ "name": "input",
+ "type": "text"
+ },
+ {
+ "name": "poiInput",
+ "type": "text",
+ "label": "Insert/Update PoI",
+ "description": "Insert or update a Point of Interest.",
+ "actionlabel": "Map Viewer Insert/Update PoI",
+ "friendcode": "poi poi-list"
+ }
+ ]
+ }
+ },
+ "outputs": {
+ "description": "List of outputs of the component. This component will be able to send data to the outputs listed here.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "required": [
+ "name",
+ "type"
+ ],
+ "properties": {
+ "name": {
+ "description": "Name that will uniquely identify this output.",
+ "type": "string"
+ },
+ "type": {
+ "description": "Type of the output. This field can only be set to `text` right now.",
+ "type": "string",
+ "enum": [
+ "text"
+ ]
+ },
+ "label": {
+ "description": "Label of the output. This field is used to show a human-readable and translatable name of the output.",
+ "type": "string"
+ },
+ "description": {
+ "description": "Text describing what is going to happen if an event is sent to this output endpoint. This description is very important for the wiring process as the user needs this information for taking decisions on how to wire widgets/operators.",
+ "type": "string"
+ },
+ "friendcode": {
+ "description": "Keyword used to tag the output endpoint, so it can be easily suggested valid conection during the wiring process.",
+ "type": "string"
+ }
+ },
+ "examples": [
+ {
+ "name": "output",
+ "type": "text"
+ },
+ {
+ "name": "poiOutput",
+ "type": "text",
+ "label": "PoI selected",
+ "description": "A PoI has been selected on the map",
+ "friendcode": "poi"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "wiring_mashup": {
+ "$comment": "extra wiring properties of mashups",
+ "type": "object",
+ "required": [],
+ "properties": {
+ "connections": {
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "required": [
+ "source",
+ "target"
+ ],
+ "properties": {
+ "source": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/endpoint"
+ }
+ ]
+ },
+ "target": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/endpoint"
+ }
+ ]
+ },
+ "readonly": {
+ "type": "boolean",
+ "default": false
+ }
+ }
+ }
+ },
+ "operators": {
+ "type": "object",
+ "default": {},
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "required": [
+ "id",
+ "name",
+ "preferences"
+ ],
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ },
+ "preferences": {
+ "type": "object",
+ "default": {},
+ "patternProperties": {
+ ".*": {
+ "type": "object",
+ "required": [
+ "value"
+ ],
+ "properties": {
+ "readonly": {
+ "type": "boolean",
+ "default": false
+ },
+ "hidden": {
+ "type": "boolean",
+ "default": false
+ },
+ "value": {
+ "type": [
+ "string",
+ "boolean",
+ "number",
+ "null"
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "wiring_visualdescription": {
+ "type": "object",
+ "required": [],
+ "properties": {
+ "behaviours": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/visualdescription_behabiours"
+ }
+ ]
+ },
+ "components": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/visualdescription_components"
+ }
+ ]
+ },
+ "connections": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/visualdescription_connections"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "visualdescription_components": {
+ "type": "array",
+ "enum": [
+ "operator",
+ "widget"
+ ],
+ "items": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [],
+ "properties": {
+ "collapsed": {
+ "type": "boolean",
+ "default": false
+ },
+ "endpoints": {
+ "type": "object",
+ "default": {},
+ "properties": {
+ "source": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "target": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "position": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/Position2D"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "visualdescription_connections": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "sourcename",
+ "targetname",
+ "sourcehandle",
+ "targethandle"
+ ],
+ "properties": {
+ "sourcename": {
+ "type": "string"
+ },
+ "targetname": {
+ "type": "string"
+ },
+ "sourcehandle": {
+ "type": [
+ "string",
+ "object"
+ ],
+ "allOf": [
+ {
+ "if": {
+ "type": "object"
+ },
+ "then": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/Position2D"
+ }
+ ]
+ },
+ "else": {
+ "enum": [
+ "auto"
+ ]
+ }
+ }
+ ]
+ },
+ "targethandle": {
+ "type": [
+ "string",
+ "object"
+ ],
+ "allOf": [
+ {
+ "if": {
+ "type": "object"
+ },
+ "then": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/Position2D"
+ }
+ ]
+ },
+ "else": {
+ "enum": [
+ "auto"
+ ]
+ }
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "visualdescription_behabiours": {
+ "default": {
+ "title": null,
+ "description": null,
+ "components": {
+ "operator": {},
+ "widget": {}
+ },
+ "connections": []
+ },
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": [
+ "title",
+ "description"
+ ],
+ "properties": {
+ "title": {
+ "type": "string"
+ },
+ "description": {
+ "type": "string"
+ },
+ "components": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/visualdescription_components"
+ }
+ ]
+ },
+ "connections": {
+ "allOf": [
+ {
+ "$ref": "#/$defs/visualdescription_connections"
+ }
+ ]
+ }
+ },
+ "additionalProperties": false
+ }
+ },
+ "translations": {
+ "description": "Translations of the component. Each key is a language code and the value is a translation object of the component elements in that language.",
+ "type": "object",
+ "default": {},
+ "patternProperties": {
+ ".*": {
+ "description": "Translation object of the component elements for a specified language.",
+ "type": "object",
+ "patternProperties": {
+ ".*": {
+ "description": "Translation of a component element for a specified language.",
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "endpoint": {
+ "description": "A wiring endpoint of a component.",
+ "type": "object",
+ "required": [
+ "type",
+ "endpoint",
+ "id"
+ ],
+ "properties": {
+ "type": {
+ "type": "string"
+ },
+ "endpoint": {
+ "type": "string"
+ },
+ "id": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ }
+ },
+ "additionalProperties": false
+ },
+ "size": {
+ "type": "string",
+ "pattern": "\\d+(\\.\\d+)?\\s*(px|%)?"
+ },
+ "version": {
+ "type": "string",
+ "pattern": "([1-9]\\d*\\.|0\\.)*([1-9]\\d*|0)((a|b|rc)[1-9]\\d*)?(-dev.*)?",
+ "examples": [
+ "1.0.0",
+ "1.0.0a1",
+ "1.0.0b2",
+ "1.0.0rc3",
+ "1.0.0-dev"
+ ]
+ },
+ "name": {
+ "type": "string",
+ "pattern": "[^/]+"
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/wirecloud/commons/utils/wgt.py b/src/wirecloud/commons/utils/wgt.py
index 1bf1b0470e..16716afed9 100644
--- a/src/wirecloud/commons/utils/wgt.py
+++ b/src/wirecloud/commons/utils/wgt.py
@@ -38,8 +38,8 @@ def __str__(self):
class WgtFile(object):
-
- _template_filename = 'config.xml'
+ _possible_template_filenames = ['config.xml', 'macconfig.json']
+ _template_filename = None
def __init__(self, _file):
self._zip = zipfile.ZipFile(_file)
@@ -49,6 +49,7 @@ def __init__(self, _file):
raise ValueError('Invalid file name: %s', filename)
if normalized_filename.startswith('/'):
raise ValueError('Invalid absolute file name: %s', filename)
+ self._set_template_filename()
@property
def namelist(self):
@@ -60,11 +61,18 @@ def get_underlying_file(self):
def read(self, path):
return self._zip.read(path)
+ def _set_template_filename(self):
+ for possible_template_file in self._possible_template_filenames:
+ if possible_template_file in self._zip.namelist():
+ self._template_filename = possible_template_file
+ return
+
def get_template(self):
try:
- return self.read(self._template_filename)
+ template_file_content = self.read(self._template_filename)
+ return template_file_content
except KeyError:
- raise InvalidContents('Missing config.xml at the root of the zipfile (wgt)')
+ raise InvalidContents('Missing config.xml or macconfig.json at the root of the zipfile (wgt)')
def extract_file(self, file_name, output_path, recreate_=False):
contents = self.read(file_name)
@@ -147,13 +155,13 @@ def update_config(self, contents):
new_fp = BytesIO()
# Copy every file from the original zipfile to the new one
- # excluding the config.xml file, that will be replaced
- filename = 'config.xml'
+ # excluding the config file, that will be replaced
+ filename = self._template_filename
with zipfile.ZipFile(new_fp, 'w') as zout:
zout.comment = self._zip.comment # preserve the comment
for item in self._zip.infolist():
- # Copy new config.xml contents
+ # Copy new config contents
if item.filename == filename:
zout.writestr(item, contents)
# Copy original files
diff --git a/src/wirecloud/platform/workspace/mashupTemplateGenerator.py b/src/wirecloud/platform/workspace/mashupTemplateGenerator.py
index 224584ecd4..4df8b70ce0 100644
--- a/src/wirecloud/platform/workspace/mashupTemplateGenerator.py
+++ b/src/wirecloud/platform/workspace/mashupTemplateGenerator.py
@@ -180,12 +180,12 @@ def build_json_template_from_workspace(options, workspace, user):
options['description'] = get_workspace_description(workspace)
if 'authors' not in options:
- options['authors'] = ({'name': str(user)},)
+ options['authors'] = [{'name': str(user)}]
elif isinstance(options['authors'], str):
options['authors'] = parse_contacts_info(options['authors'])
if 'contributors' not in options:
- options['contributors'] = ()
+ options['contributors'] = []
elif isinstance(options['contributors'], str):
options['contributors'] = parse_contacts_info(options['contributors'])
@@ -252,6 +252,7 @@ def build_json_template_from_workspace(options, workspace, user):
options['wiring']['operators'] = {}
for operator_id, operator in wiring_status['operators'].items():
operator_data = {
+ 'id': operator_id,
'name': operator['name'],
'preferences': {},
}
diff --git a/src/wirecloud/platform/workspace/views.py b/src/wirecloud/platform/workspace/views.py
index 52d25a22b8..30fab41a30 100644
--- a/src/wirecloud/platform/workspace/views.py
+++ b/src/wirecloud/platform/workspace/views.py
@@ -483,12 +483,12 @@ def create(self, request, workspace_id):
extra_files.append(('DESCRIPTION.md', BytesIO(options['longdescription'].encode('utf-8'))))
options['longdescription'] = 'DESCRIPTION.md'
- description = build_xml_template_from_workspace(options, workspace, request.user)
+ description = build_json_template_from_workspace(options, workspace, request.user)
# Build mashup wgt file
f = BytesIO()
zf = zipfile.ZipFile(f, 'w')
- zf.writestr('config.xml', description.encode('utf-8'))
+ zf.writestr('macconfig.json', json.dumps(description, indent = 4))
for filename, extra_file in extra_files:
zf.writestr(filename, extra_file.read())
for resource_info in options['embedded']: