Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
O
OpnSense
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kulya
OpnSense
Commits
d9e78ccb
Commit
d9e78ccb
authored
Jan 26, 2015
by
Ad Schellevis
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
work in progress new mvc framework for gui part
parent
9af57742
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
577 additions
and
10 deletions
+577
-10
PageController.php
...se/mvc/app/controllers/OPNsense/Sample/PageController.php
+1
-0
BaseModel.php
src/opnsense/mvc/app/models/OPNsense/Base/BaseModel.php
+215
-0
ArrayField.php
...se/mvc/app/models/OPNsense/Base/FieldTypes/ArrayField.php
+72
-0
BaseField.php
...nse/mvc/app/models/OPNsense/Base/FieldTypes/BaseField.php
+149
-0
ContainerField.php
...vc/app/models/OPNsense/Base/FieldTypes/ContainerField.php
+37
-0
TextField.php
...nse/mvc/app/models/OPNsense/Base/FieldTypes/TextField.php
+55
-0
ModelException.php
src/opnsense/mvc/app/models/OPNsense/Base/ModelException.php
+8
-9
Sample.php
src/opnsense/mvc/app/models/OPNsense/Sample/Sample.php
+6
-1
Sample.xml
src/opnsense/mvc/app/models/OPNsense/Sample/Sample.xml
+29
-0
page.show.volt
src/opnsense/mvc/app/views/OPNsense/Sample/page.show.volt
+5
-0
No files found.
src/opnsense/mvc/app/controllers/OPNsense/Sample/PageController.php
View file @
d9e78ccb
...
...
@@ -46,6 +46,7 @@ class PageController extends ControllerBase
$sample
=
new
Sample
();
$this
->
view
->
title
=
$sample
->
title
;
$this
->
view
->
items
=
array
(
array
(
'field_name'
=>
'test'
,
'field_content'
=>
'1234567'
,
'field_type'
=>
"text"
)
);
$this
->
view
->
data
=
$sample
;
// Pass the $postId parameter to the view
//$this->view->setVar("postId", $postId);
...
...
src/opnsense/mvc/app/models/OPNsense/Base/BaseModel.php
0 → 100644
View file @
d9e78ccb
<?php
/*
# Copyright (C) 2015 Deciso B.V.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------------
package : Frontend Model Base
function: implements base model to bind config and definition to object
*/
namespace
OPNsense\Base
;
use
OPNsense\Base\FieldTypes\ArrayField
;
use
OPNsense\Core\Config
;
abstract
class
BaseModel
{
/**
* @var null|BaseField internal model data structure, should contain Field type objects
*/
private
$internalData
=
null
;
/**
* place where the real data in the config.xml should live
* @var string
*/
private
$internal_mountpoint
=
''
;
private
$internalConfigHandle
=
null
;
/**
* If the model needs a custom initializer, override this init() method
* Default behaviour is to do nothing in this init.
*/
protected
function
init
()
{
return
;
}
/**
* @param $xml|SimpleXMLElement model xml data (from items section)
* @param $config_data|SimpleXMLElement (current) config data
* @param $internal_data output structure using FieldTypes, rootnode is internalData
* @throws ModelException parse error
*/
private
function
parseXml
(
$xml
,
&
$config_data
,
&
$internal_data
)
{
foreach
(
$xml
->
children
()
as
$xmlNode
)
{
$tagName
=
$xmlNode
->
getName
();
// every item results in a Field type object, the first step is to determine which object to create
// based on the input model spec
$fieldObject
=
null
;
$classname
=
"OPNsense
\\
Base
\\
FieldTypes
\\
"
.
$xmlNode
->
attributes
()[
"type"
];
if
(
class_exists
(
$classname
))
{
// construct field type object
$field_rfcls
=
new
\ReflectionClass
(
$classname
);
if
(
!
$field_rfcls
->
getParentClass
()
->
name
==
'OPNsense\Base\FieldTypes\BaseField'
)
{
// class found, but of wrong type. raise an exception.
throw
new
ModelException
(
"class "
.
$field_rfcls
->
name
.
" of wrong type in model definition"
);
}
}
else
{
// no type defined, so this must be a standard container (without content)
$field_rfcls
=
new
\ReflectionClass
(
'OPNsense\Base\FieldTypes\ContainerField'
);
}
// generate full object name ( section.section.field syntax ) and create new Field
if
(
$internal_data
->
__reference
==
""
)
{
$new_ref
=
$tagName
;
}
else
{
$new_ref
=
$internal_data
->
__reference
.
"."
.
$tagName
;
}
$fieldObject
=
$field_rfcls
->
newInstance
(
$new_ref
);
// now add content to this model (recursive)
if
(
$fieldObject
->
isContainer
()
==
false
)
{
$internal_data
->
addChildNode
(
$tagName
,
$fieldObject
);
if
(
$xmlNode
->
count
()
>
0
)
{
// if fieldtype contains properties, try to call the setters
foreach
(
$xmlNode
->
children
()
as
$fieldMethod
)
{
$param_value
=
$fieldMethod
->
__toString
()
;
$method_name
=
"set"
.
$fieldMethod
->
getName
();
if
(
$field_rfcls
->
hasMethod
(
$method_name
))
{
$fieldObject
->
$method_name
(
$param_value
);
}
}
}
if
(
$config_data
!=
null
&&
isset
(
$config_data
->
$tagName
))
{
// set field content from config (if available)
$fieldObject
->
setValue
(
$config_data
->
$tagName
->
__toString
());
}
}
else
{
// add new child node container, always try to pass config data
if
(
$config_data
!=
null
&&
isset
(
$config_data
->
$tagName
))
{
$config_section_data
=
$config_data
->
$tagName
;
}
else
{
$config_section_data
=
null
;
}
if
(
$fieldObject
instanceof
ArrayField
)
{
// handle Array types, recurring items
if
(
$config_section_data
!=
null
)
{
$counter
=
0
;
foreach
(
$config_section_data
as
$conf_section
)
{
$child_node
=
new
ArrayField
(
$fieldObject
->
__reference
.
"."
.
(
$counter
++
));
$this
->
parseXml
(
$xmlNode
,
$conf_section
,
$child_node
);
$fieldObject
->
addChildNode
(
null
,
$child_node
);
}
}
else
{
$child_node
=
new
ArrayField
(
$fieldObject
->
__reference
.
".0"
);
$child_node
->
setInternalEmptyStatus
(
true
);
$this
->
parseXml
(
$xmlNode
,
$config_section_data
,
$child_node
);
$fieldObject
->
addChildNode
(
null
,
$child_node
);
}
}
else
{
$this
->
parseXml
(
$xmlNode
,
$config_section_data
,
$fieldObject
);
}
$internal_data
->
addChildNode
(
$xmlNode
->
getName
(),
$fieldObject
);
}
}
}
/**
* Construct new model type, using it's own xml template
* @throws ModelException if the model xml is not found or invalid
*/
public
function
__construct
()
{
// setup config handle to singleton config singleton
$this
->
internalConfigHandle
=
Config
::
getInstance
();
// init new root node, all details are linked to this
$this
->
internalData
=
new
FieldTypes\ContainerField
();
// determine our caller's filename and try to find the model definition xml
// throw error on failure
$class_info
=
new
\ReflectionClass
(
$this
);
$model_filename
=
substr
(
$class_info
->
getFileName
(),
0
,
strlen
(
$class_info
->
getFileName
())
-
3
)
.
"xml"
;
if
(
!
file_exists
(
$model_filename
))
{
throw
new
ModelException
(
'model xml '
.
$model_filename
.
' missing'
)
;
}
$model_xml
=
simplexml_load_file
(
$model_filename
);
if
(
$model_xml
===
false
)
{
throw
new
ModelException
(
'model xml '
.
$model_filename
.
' not valid'
)
;
}
if
(
$model_xml
->
getName
()
!=
"model"
)
{
throw
new
ModelException
(
'model xml '
.
$model_filename
.
' seems to be of wrong type'
)
;
}
// use an xpath expression to find the root of our model in the config.xml file
// if found, convert the data to a simple structure (or create an empty array)
$tmp_config_data
=
$this
->
internalConfigHandle
->
xpath
(
$model_xml
->
mount
);
if
(
$tmp_config_data
->
length
>
0
)
{
$config_array
=
simplexml_import_dom
(
$tmp_config_data
->
item
(
0
))
;
}
else
{
$config_array
=
array
();
}
// We've loaded the model template, now let's parse it into this object
$this
->
parseXml
(
$model_xml
->
items
,
$config_array
,
$this
->
internalData
)
;
//print_r($this->internalData);
// call Model initializer
$this
->
init
();
}
/**
* reflect getter to internalData (ContainerField)
* @param $name property name
* @return mixed
*/
public
function
__get
(
$name
)
{
return
$this
->
internalData
->
$name
;
}
/**
* reflect setter to internalData (ContainerField)
* @param $name property name
* @param $value property value
*/
public
function
__set
(
$name
,
$value
)
{
$this
->
internalData
->
$name
=
$value
;
}
}
\ No newline at end of file
src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/ArrayField.php
0 → 100644
View file @
d9e78ccb
<?php
/*
# Copyright (C) 2015 Deciso B.V.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------------
package : Frontend Model Base
function:
*/
namespace
OPNsense\Base\FieldTypes
;
class
ArrayField
extends
BaseField
{
/**
* @var bool is this array empty ( only filled with defaults)
*/
private
$internalEmptyStatus
=
false
;
/**
* add Childnode (list)
* @param $name property name
* @param $node content (must be of type BaseField)
*/
public
function
addChildNode
(
$name
,
$node
)
{
if
(
$name
==
null
)
{
// index item
$this
->
internalChildnodes
[]
=
$node
;
}
else
{
$this
->
internalChildnodes
[
$name
]
=
$node
;
}
}
/**
* @return bool is empty array (only filled for template structure)
*/
public
function
isEmpty
()
{
return
$this
->
internalEmptyStatus
;
}
/**
* @param $status|bool set empty (status boolean)
*/
public
function
setInternalEmptyStatus
(
$status
)
{
$this
->
internalEmptyStatus
=
$status
;
}
}
\ No newline at end of file
src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/BaseField.php
0 → 100644
View file @
d9e78ccb
<?php
/*
# Copyright (C) 2015 Deciso B.V.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------------
package : Frontend Model Base
function:
*/
namespace
OPNsense\Base\FieldTypes
;
abstract
class
BaseField
{
/**
* @var array child nodes
*/
protected
$internalChildnodes
=
array
();
/**
* @var bool marks if this is a data node or a container
*/
protected
$internalIsContainer
=
true
;
/**
* @var null|string node value
*/
protected
$internalValue
=
""
;
/**
* @var string direct reference to this field in the model object
*/
protected
$internalReference
=
""
;
/**
* @return bool returns if this a container type object (no data)
*/
public
function
isContainer
()
{
return
$this
->
internalIsContainer
;
}
/**
* default constructor
* @param null|string $ref direct reference to this object
*/
public
function
__construct
(
$ref
=
null
)
{
$this
->
internalReference
=
$ref
;
}
/**
* @param $name property name
* @param $node content (must be of type BaseField)
*/
public
function
addChildNode
(
$name
,
$node
)
{
$this
->
internalChildnodes
[
$name
]
=
$node
;
}
/**
* Reflect default getter to internal child nodes.
* Implements the special attribute __items to return all items and __reference to identify the field in this model.
* @param $name property name
* @return mixed
*/
public
function
__get
(
$name
)
{
if
(
array_key_exists
(
$name
,
$this
->
internalChildnodes
))
{
return
$this
->
internalChildnodes
[
$name
];
}
elseif
(
$name
==
'__items'
)
{
return
$this
->
internalChildnodes
;
}
elseif
(
$name
==
'__reference'
)
{
return
$this
->
internalReference
;
}
else
{
// not found
return
null
;
}
}
/**
* reflect default setter to internal child nodes
* @param $name|string property name
* @param $value|string property value
*/
public
function
__set
(
$name
,
$value
)
{
if
(
array_key_exists
(
$name
,
$this
->
internalChildnodes
))
{
$this
->
internalChildnodes
[
$name
]
->
setValue
(
$value
);
}
}
public
function
__toString
()
{
return
$this
->
internalValue
;
}
/**
* set Default field value
* @param $value default value
*/
public
function
setDefault
(
$value
)
{
$this
->
internalValue
=
$value
;
}
/**
* default setter
* @param $value set field value
*/
public
function
setValue
(
$value
)
{
$this
->
internalValue
=
$value
;
}
/**
* @return array child items
*/
public
function
getChildren
()
{
return
$this
->
internalChildnodes
;
}
}
\ No newline at end of file
src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/ContainerField.php
0 → 100644
View file @
d9e78ccb
<?php
/*
# Copyright (C) 2015 Deciso B.V.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------------
package : Frontend Model Base
function:
*/
namespace
OPNsense\Base\FieldTypes
;
class
ContainerField
extends
BaseField
{
}
\ No newline at end of file
src/opnsense/mvc/app/models/OPNsense/Base/FieldTypes/TextField.php
0 → 100644
View file @
d9e78ccb
<?php
/*
# Copyright (C) 2015 Deciso B.V.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------------
package : Frontend Model Base
function:
*/
namespace
OPNsense\Base\FieldTypes
;
class
TextField
extends
BaseField
{
/**
* @var bool marks if this is a data node or a container
*/
protected
$internalIsContainer
=
false
;
/**
* @var null|string validation mask (regex)
*/
protected
$internalMask
=
null
;
/**
* set validation mask
* @param $value regexp validation mask
*/
public
function
setMask
(
$value
)
{
$this
->
internalMask
=
$value
;
}
}
\ No newline at end of file
src/opnsense/mvc/app/models/OPNsense/Base/Model.php
→
src/opnsense/mvc/app/models/OPNsense/Base/Model
Exception
.php
View file @
d9e78ccb
...
...
@@ -27,17 +27,16 @@
--------------------------------------------------------------------------------------
package : Frontend Model Base
function:
implements base model to bind config and definition to object
function:
Basic Exception class for Model initialization
*/
namespace
OPNsense\Base
;
abstract
class
BaseModel
/**
* Class BaseModelException
* @package Base
*/
class
ModelException
extends
\Exception
{
public
function
__construct
()
{
$class_info
=
new
\ReflectionClass
(
$this
);
$class_info
->
getFileName
();
}
}
\ No newline at end of file
src/opnsense/mvc/app/models/OPNsense/Sample/Sample.php
View file @
d9e78ccb
...
...
@@ -34,5 +34,10 @@ use OPNsense\Base\BaseModel;
class
Sample
extends
BaseModel
{
public
$title
=
"123456"
;
// protected function init()
// {
// print("\nxXx\n");
// }
}
src/opnsense/mvc/app/models/OPNsense/Sample/Sample.xml
0 → 100644
View file @
d9e78ccb
<model>
<mount>
//test/sample
</mount>
<description>
Sample model
</description>
<items>
<tag1
type=
"TextField"
>
<Mask>
[A-Z]
</Mask>
</tag1>
<tagX>
<tag1
type=
"TextField"
>
<Default>
test1234
</Default>
</tag1>
<tag2>
<tagZ></tagZ>
</tag2>
</tagX>
<childnodes>
<section
type=
"ArrayField"
>
<node1
type=
"TextField"
/>
<node2
type=
"TextField"
/>
</section>
<section1
type=
"ArrayField"
>
<nodeX
type=
"TextField"
></nodeX>
<nodeZ
type=
"TextField"
></nodeZ>
</section1>
</childnodes>
</items>
</model>
\ No newline at end of file
src/opnsense/mvc/app/views/OPNsense/Sample/page.show.volt
View file @
d9e78ccb
...
...
@@ -4,3 +4,8 @@
{% for item in items %}
{{ partial('layout_partials/std_input_field',item) }}
{% endfor %}
<br>
{% for section in data.childnodes.section.__items %}
{{ section.node1 }} <br>
{% endfor %}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment