Skip to content
Snippets Groups Projects
Commit eca5ff59 authored by Aaron Klump's avatar Aaron Klump
Browse files

Merge branch 'release-7.x-2.3-rc2'

parents ae305509 797e0d4d
No related branches found
No related tags found
No related merge requests found
<h2>Summary</h2>
<p>A complete solution for entering, storing and working with XML and Drupal. The versatility of this module spans from simply storing and displaying informational XML to your visitors, to using XML as a configuration mechanism and never displaying the actual XML to the end user.</p>
<p>The module provides API functions for working with XML, which extend PHP's own <a href="http://php.net/manual/en/book.simplexml.php">SimpleXML</a>, and defines an XML field type, widget and formatters. Starting in version 1.6, the XML widget leverages <a href="http://codemirror.net/">CodeMirror</a> for awesome data entry. You can format the XML to be hidden, display in color coded, escaped HTML, print directly as hidden XML, or show as a read-only text area for easy copy-and-paste. The formatter you choose will depend on your use case.</p>
<p>The module also adds <code>theme_xml()</code> for displaying XML in colorized, HTML entities, and adds a new form element: <code>xmltext</code>.</p>
<h2>Highlights</h2>
<ul>
<li>Create fields on your entities to <strong>store XML data</strong>, which validate upon form submission.</li>
<li>Use the <strong>CodeMirror</strong> widget for awesome inline XML entry (as of 1.6).</li>
<li><strong>XML Schema Support</strong> (as of 1.7). You may choose to define a schema for each field instance; your XML will validate against this schema when the entity edit form is saved.</li>
<li><strong>Extensive developer functions</strong> for working with XML and XML as fields attached to entities. Functions include: sorting, outputting, validating and testing XML elements.</li>
<li><strong>Superior CDATA handling</strong> from that of standard SimpleXML. See Advanced Help documents for specific info on this.</li>
<li>Automatic CDATA escaping when enabled.</li>
<li><strong>FAPI Integration</strong> with XML elements provided.</li>
</ul>
<h2>Requirements</h2>
<ul>
<li>SimpleXML <a href="http://www.php.net/manual/en/book.simplexml.php">http://www.php.net/manual/en/book.simplexml.php</a></li>
<li>The SimpleXML extension requires PHP 5.</li>
</ul>
<h2>Installation</h2>
<ul>
<li>Download and unzip this module into your modules directory.</li>
<li>Goto Administer > Site Building > Modules and enable this module.</li>
<li>Optionally, enable <code>xml_field_codemirror</code> to have an XML widget available.</li>
</ul>
<h3>CodeMirror XML Widget (7.x-1.6+)</h3>
<p>To use the XML widget (using <a href="http://codemirror.net">CodeMirror</a>), you must do the following:</p>
<ol>
<li>Download the CodeMirror package (Tested with versions 3.x and 4.x) and unzip in to your libraries folder <a href="http://codemirror.net/codemirror.zip">http://codemirror.net/codemirror.zip</a>.</li>
<li>Rename the folder to simply <code>codemirror</code></li>
<li>Enable the included module <code>CodeMirror XML Widget</code> (<code>xml_field_codemirror</code>). You do not need to install <a href="http://drupal.org/project/codemirror">http://drupal.org/project/codemirror</a></li>
<li>Make sure <code>codemirror.js</code> is located in the following location: <code>libraries/codemirror/lib/codemirror.js</code>.</li>
<li>Check <code>admin/reports/status</code> and you should see the CodeMirror version number.</li>
<li>Note that after setting an XML field's widget to CodeMirror XML, there are additional settings available under the Edit tab. This includes the CodeMirror color scheme (or theme).</li>
</ol>
<h4>CodeMirror Global Default Theme</h4>
<p>Code mirror uses themes for colorizing the input widget. You can set a global theme and save yourself from lots of clicks by setting a <code>$conf</code> variable in your <code>settings.php</code>. To set a global default theme add the following line to your <code>settings.php</code> file:</p>
<pre><code>$conf['xml_field_codemirror_default_theme'] = 'cobalt'
</code></pre>
<p>You do not have to do this in <code>settings.php</code>, rather you can do it in your own custom module using a hook. To do this using a custom module hook, implement the following in your custom module:</p>
<pre><code>function my_module_xml_field_codemirror_defaults_alter(&amp;$config) {
$config['theme'] = 'cobalt';
}
</code></pre>
<p>Do either of these steps BEFORE creating fields and all your fields will default to this value.</p>
<h4>API: CodeMirror Options</h4>
<p>Check out <code>xml_field_codemirror.api.php</code> for API options.</p>
<h4>Known Issues</h4>
<ol>
<li>XML fields inside collapsed fieldsets may not show upon expansion; usually clicking the xml field brings it back.</li>
</ol>
<h2>Upgrade Path</h2>
<h3>to 7.x-1.6</h3>
<p>To begin using the XML widget introduced in version 1.6:</p>
<ol>
<li>Upgrade XML Field to version 7.x-1.6</li>
<li>Install the CodeMirror XML Widget (7.x-1.6+) as described above.</li>
<li>Edit each XML field and change the widget to CodeMirror XML. Optionally, adjust the theme under the field's Edit tab.</li>
<li>Edit a node to see the new widget in action.</li>
</ol>
<h2>Configuration</h2>
<ul>
<li>Create an XML field and attach it to an entity, as with any field type.</li>
</ul>
<h2>FAPI</h2>
<h3>xmltext</h3>
<p>This module adds a new form element called <code>xmltext</code> which does xml validation on form submit.</p>
<h2>Usage</h2>
<p>There are essentially three use case modes for XML and this module.</p>
<ol>
<li>Output the XML so it can been seen in the browser window, by first converting it to html entities. For visual display of XML.</li>
<li>Output the XML to the browser directly to influence the actual page load in some way. SECURITY WARNING, SEE BELOW!</li>
<li>Hide the field (Manage Display) and use <code>xml_field_xml()</code> and <code>xml_field()</code> to extract the xml data, from within your custom module. This latter usage allows for very sophisticated layout and theming, while still allowing admins to modify content. (See Advanced Use Case)</li>
</ol>
<h3>Simple Use Case</h3>
<p>You have a node type, which needs to display XML data below the body content and you just want an easy way to do it without having to write your own validation and sanitization code. How then? Install the module, create a field, and set the formatter to XML for display. You're done! You have instant XML validation and sanitized output. Viola.</p>
<h3>Advanced Use Case</h3>
<p>As a developer, you designate a page node to be the foundation of a page, as it provides a solid page callback, menu link, title and body text.</p>
<p>To this page node you add a single field called field_xml_page_data In hook_node_view_alter() you add extra content to $build, content that is generated dynamically based on any number of rules.</p>
<p>The problem is that you want to allow a title to this content and maybe a footer, text which needs to be accessible for modification by an content manager. How do you do it? You create an XML representation of the editable content, which is intuitive to a content manager with only a little knowledge of XML, something like this:</p>
<pre><code>&lt;page&gt;
&lt;title&gt;The Lone Ranger&lt;/title&gt;
&lt;footer format="1"&gt;Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla at massa sed nulla consectetur malesuada.&lt;/description&gt;
&lt;/page&gt;
</code></pre>
<p>Now back in hook_node_view_alter() you will parse the xml and use the fields to insert the title and the description (after sanitizing as needed), like this:</p>
<pre><code>$xml = $node-&gt;field_xml_page_data['und'][0]['xml'];
$title = xml_field($xml, 'title');
$format = xml_field($xml, 'description', 'format');
$description = xml_field($xml, 'description', NULL, array('check_markup', $format));
</code></pre>
<p>With this method you have a very robust way to allow disparate elements on the page to still be accessible to content managers, while maintaining absolute theme control at the PHP level. And, the beauty of XML is that you can make it as simple and descriptive as needed or as technical and nested as you desire since the tags are completely arbitrary (unlike trying to get content managers to understand HTML tags!)</p>
<h3>Leveraging <code>xml_field_xml()</code></h3>
<p>This function is at the heart of the module and should be understood by
developers. It will extract the xml from many different input sources and (used in conjunction with xml_field()) allows you to save code. As a brief example, the Advanced Case will be rewritten here, but there are assumptions to be made which allow this to happen. The first assumption is that there is only one XML field in the entity. The second assumption is that the entity is not translated into other languages. And the last assumption is that the field only allows one value. If all of these are true then we can simply pass the entity, in this case $node to our api funtions and save steps. Observe...</p>
<pre><code>$title = xml_field($node, 'title');
$format = xml_field($node, 'description', 'format');
$description = xml_field($node, 'description', NULL, array('check_markup', $format));
</code></pre>
<p>Study the docblocks in the code for more info.</p>
<h2>API</h2>
<h3>The main API Functions:</h3>
<ul>
<li><code>xml_field_xml()</code>: to obtain an XML object</li>
<li><code>xml_field()</code>: to access XML values</li>
<li><code>xml_field_format()</code>: to obtain a string version of any XML</li>
<li><code>xml_field_output()</code>: to output XML to the browser</li>
<li><code>xml_field_sort()</code>: sort children by an attribute</li>
</ul>
<h3>Additional API functions:</h3>
<ul>
<li><code>xml_field_boolean</code>: test for a boolean value</li>
<li><code>xml_field_has()</code>: verify if a node or attribute exists, even if empty</li>
<li><code>xml_field_is_valid_xml_string()</code></li>
<li><code>xml_field_load_string()</code>: use to create a simpleXMLElement compatible object instead of <code>simplexml_load_string()</code></li>
<li><code>xml_field_xml_fields()</code></li>
<li><code>xml_field_append</code>: append an xml node inside another</li>
<li><code>theme_xml()</code></li>
</ul>
<h2>Security Warning!</h2>
<p>This module contains a raw XML formatter that allows the output of unfiltered text directly to the browser, if malicious users are allowed to enter data into a field with this formatter, they could create a security problem. This is similiar to giving the PHP filter to untrusted users. The result could be the same.</p>
<p>This formatter can be used beneficially too, by trusted users, so it is included, however you must understand what you are doing if you employ it.</p>
<h2>Similar Projects</h2>
<ul>
<li><a href="http://drupal.org/project/xmlcontent">http://drupal.org/project/xmlcontent</a></li>
<li><a href="http://drupal.org/project/codemirror">http://drupal.org/project/codemirror</a></li>
</ul>
<h2>Sponsored by</h2>
<p><a href="http://www.intheloftstudios.com/">In the Loft Studios</a> and <a href="http://www.globalonenessproject.org">Global Oneness Project</a></p>
<h2>More Examples</h2>
<h3>Example A</h3>
<p>This example shows how to iterate over multiple nodes and also to access
properties.</p>
<pre><code>&lt;page&gt;
&lt;button color="green"&gt;Order your free DVDs and host a screening&lt;/button&gt;
&lt;button color="beige" class="more-link"&gt;Learn more about screenings&lt;/button&gt;
&lt;/page&gt;
&lt;?php
// Add the buttons
$xml = xml_field_xml($node);
$build['buttons'] = $class = array();
foreach ($xml-&gt;button as $button) {
$class[] = 'button-link';
$class[] = xml_field($button, NULL, 'color');
$class[] = xml_field($button, NULL, 'class');
$build['buttons'][] = array('#markup' =&gt; l(xml_field($button), 'node/add/screening', array(
'attributes' =&gt; array(
'class' =&gt; $class
),
)));
}
?&gt;
</code></pre>
<h3>Example B</h3>
<p>This example shows how to populate two links' text using an XML field.</p>
<pre><code>&lt;page&gt;
&lt;dvd_button&gt;Order your free DVDs and host a screening&lt;/dvd_button&gt;
&lt;learn_button&gt;Learn more about screenings&lt;/learn_button&gt;
&lt;/page&gt;
&lt;?php
$build['buttons'] = array()
if ($title = xml_field($node, 'dvd_button')) {
$build['buttons'][] = array('#markup' =&gt; l($title, 'node/add/screening', array(
'attributes' =&gt; array(
'class' =&gt; array('button-link', 'green'),
),
)));
}
if ($title = xml_field($node, 'learn_button')) {
$build['buttons'][] = array('#markup' =&gt; l($title, 'node/2186', array(
'attributes' =&gt; array(
'class' =&gt; array('button-link', 'beige', 'more-link'),
),
)));
}
?&gt;
</code></pre>
<h2>Contact</h2>
<p>In the Loft Studios<br />
Aaron Klump<br />
PO Box 29294 Bellingham, WA 98228-1294<br />
aim: theloft101<br />
skype: intheloftstudios</p>
<p><a href="http://www.InTheLoftStudios.com">http://www.InTheLoftStudios.com</a></p>
<h2>Problems with CDATA in SimpleXML</h2>
<p><a href="http://blog.evandavey.com/2008/04/how-to-fix-simplexml-cdata-problem-in-php.html">http://blog.evandavey.com/2008/04/how-to-fix-simplexml-cdata-problem-in-php.html</a>
<a href="http://stackoverflow.com/questions/2970602/php-how-to-handle-cdata-with-simplexmlelement">http://stackoverflow.com/questions/2970602/php-how-to-handle-cdata-with-simplexmlelement</a>
<a href="http://stackoverflow.com/questions/6260224/how-to-write-cdata-using-simplexmlelement">http://stackoverflow.com/questions/6260224/how-to-write-cdata-using-simplexmlelement</a></p>
<h2>Comparison against SimpleXML</h2>
<h3>Doesn't print out the cdata correctly</h3>
<p>This code snippet:</p>
<pre><code>$xml = simplexml_load_string('&lt;demo/&gt;');
$xml-&gt;addChild('cdata', '&lt;![CDATA[&lt;strong&gt;title&lt;/strong&gt;]]&gt;');
print $xml-&gt;asXml();
</code></pre>
<p>Prints this out and transforms the cdata into escaped chars.</p>
<pre><code>&lt;?xml version="1.0"?&gt;
&lt;demo&gt;&lt;cdata&gt;&amp;lt;![CDATA[&amp;lt;strong&amp;gt;title&amp;lt;/strong&amp;gt;]]&amp;gt;&lt;/cdata&gt;&lt;/demo&gt;
</code></pre>
<p>However the extension class in this module handles this, observe:</p>
<pre><code>$xml = xml_field_load_string('&lt;demo/&gt;');
$xml-&gt;addChild('cdata', '&lt;![CDATA[&lt;strong&gt;title&lt;/strong&gt;]]&gt;');
print $xml-&gt;asXml();
</code></pre>
<p>Will yield what you'd expect:</p>
<pre><code>&lt;?xml version="1.0"?&gt;
&lt;demo&gt;&lt;cdata&gt;&lt;![CDATA[&lt;strong&gt;title&lt;/strong&gt;]]&gt;&lt;/cdata&gt;&lt;/demo&gt;
</code></pre>
<h3>Automatic CDATA escaping when enabled.</h3>
<p>This code snippet:</p>
<pre><code>&lt;?php
$xml = simplexml_load_string('&lt;demo/&gt;');
$xml-&gt;addChild('special', '&lt;strong&gt;Do&amp;mdash;Re&lt;/strong&gt;');
print $xml-&gt;asXml();
</code></pre>
<p>Yields the following output:</p>
<pre><code>&lt;?xml version="1.0"?&gt;
&lt;demo&gt;&lt;special&gt;&amp;lt;strong&amp;gt;Do&amp;mdash;Re&amp;lt;/strong&amp;gt;&lt;/special&gt;&lt;/demo&gt;
</code></pre>
<p>This code snippet (having enabled via <code>autoCData</code>), using our extension class: <code>xmlFieldXMLElement</code>:</p>
<pre><code>&lt;?php
$xml = xml_field_load_string('&lt;demo/&gt;');
// Be aware this is a static setting and affects ALL instances.
$xml::setConfig('autoCData', TRUE);
$xml-&gt;addChild('special', '&lt;strong&gt;Do&amp;mdash;Re&lt;/strong&gt;');
print $xml-&gt;asXml();
</code></pre>
<p>Will automatically wrap values that include <a href="http://xml.silmaril.ie/specials.html">the 5 special chars</a> with the CDATA tag:</p>
<pre><code>&lt;?xml version="1.0"?&gt;
&lt;demo&gt;&lt;special&gt;&lt;![CDATA[&lt;strong&gt;Do&amp;mdash;Re&lt;/strong&gt;]]&gt;&lt;/special&gt;&lt;/demo&gt;
</code></pre>
<p><strong>Be aware that this is a setting for all instances and should be disabled after use, unless you intend for a constant on.</strong></p>
<h2>CDATA wrap a string</h2>
<p>You can wrap a string with cdata by passing it through the static cdata method like this:</p>
<pre><code>$wrapped = xmlFieldXMLElement::cdata('Pizza', TRUE);
</code></pre>
<p>This will return:</p>
<pre><code>&lt;![CDATA[Pizza]]&gt;
</code></pre>
<p>Obviously there is no need for <em>Pizza</em> to be wrapped, which is why the second argument is set to TRUE. If you omit the second argument (which forces the wrapping), only strings that need it will be wrapped, e.g. <code>&lt;strong&gt;title&lt;/strong&gt;</code>.</p>
<h2>Theme functions</h2>
<p>The following theme functions are available for safely writing XML to the screen. We're assuming you want to present the XML as viewable code, not functional XML.</p>
<pre><code>theme_xml
theme_xmltext
theme_xml_codemirror (you must enable xml_field_codemirror module)
</code></pre>
<h2>Field formatters</h2>
<p>Using the following field formatters allow the safe output of XML code as well.</p>
<pre><code>Escaped XML for display
Read-only Textarea
Read-only CodeMirror XML
</code></pre>
<h2>xmltext</h2>
<p>A FAPI element using a textarea with xml validation.</p>
<pre><code>&lt;?php
$form['xml_config'] = array(
'#type' =&gt; 'xmltext',
'#title' =&gt; 'XML config',
'#default_value' =&gt; variable_get('xml_config', ''),
'#xml_schema' =&gt; '&lt;xs:schema&gt;...&lt;/xs:schema&gt;',
'#xml_example' =&gt; '&lt;something&gt;...&lt;/something&gt;',
);
</code></pre>
<h2>xml_codemirror</h2>
<p>Enable <em>xml_field_codemirror</em> module to introduce this FAPI element, which creates a <a href="http://codemirror.net/index.html">CodeMirror</a> input field with xml validation.</p>
<p>Refer to the <a href="http://codemirror.net/doc/manual.html#config">online API</a> for more info.</p>
<p>You may alter the global default options using <em>hook_xml_field_codemirror_config_alter()</em>. For fields you may alter some configuration options in the field UI. To control the CodeMirror options at the element level add/alter the key <code>#codemirror_options</code>:</p>
<pre><code>&lt;?php
$form['xml_config'] = array(
'#type' =&gt; 'xml_codemirror',
'#title' =&gt; 'XML config',
'#default_value' =&gt; variable_get('xml_config', ''),
'#codemirror_options' =&gt; array(
'tabSize' =&gt; 2,
);
);
</code></pre>
<p>In these examples, <code>$xml</code> can be either a string or an object. <code>$selector</code> should be a string, e.g. "child". We wrap <code>$xml</code> in <code>xml_field_xml()</code> to make sure that we have an xml object.</p>
<h2>Iterating over attributes</h2>
<pre><code> if (xml_field_has($xml, $selector)) {
$attributes = xml_field_xml($xml)-&gt;{$selector}-&gt;attributes();
foreach (array_keys($attributes) as $name) {
...
}
}
</code></pre>
<h2>Iterating over children</h2>
<pre><code> if (xml_field_has($xml, $selector)) {
$attributes = xml_field_xml($xml)-&gt;{$selector}-&gt;children();
foreach (array_keys($attributes) as $name) {
...
}
}
</code></pre>
<p>To work with schemas, you must enable the <em>xml_field_extras</em> module.</p>
<h2>FAPI</h2>
<ul>
<li>Whenever <em>#xml_schema</em> has a value the element value will be validated against it when forms are submitted.</li>
<li><p>To hide the XSD on forms you will need to add the following to the element.</p>
<pre><code>'#xml_schema_show' = FALSE
</code></pre></li>
</ul>
<h3>xmltext</h3>
<h2>FAPI Element: xml_codemirror</h2>
<p>In addition to the field instance default and schema (which are stored in the database via the Field API mechanism), this module allows for much finer-grained control of defaults and schemas, even down to the individual entity or even entity field level, e.g. <code>$node-&gt;field_xml</code>.</p>
<p>For this type of control, defaults and schemas are provided using include files in either your custom module or the public files directory. In both cases, we'll use the familiar form of suggestion filename convention found throughout Drupal.</p>
<p>For schema definitions use this cascade:</p>
<pre><code>entity_type--entity_id--field_id.xsd
entity_type--entity_id.xsd
entity_type--bundle_type--field_id.xsd
entity_type--bundle_type.xsd
entity_type--field_id.xsd
entity_type.xsd
default.xsd
</code></pre>
<p>For field defaults use this cascade:</p>
<pre><code>entity_type--entity_id--field_id.xml
entity_type--entity_id.xml
entity_type--bundle_type--field_id.xml
entity_type--bundle_type.xml
entity_type--field_id.xml
entity_type.xml
default.xml
</code></pre>
<h2>Gotchas</h2>
<ol>
<li>Did you implement <code>hook_xml_field_api()</code>? </li>
<li>Be sure to clear cache after adding/removing files.</li>
<li>Did you use a <strong>double-hyphen</strong> in the filename?</li>
<li>When editing a field instance, you will see a form field called <em>XML Schema</em>. This form field will disappear and become irrelevant if you add an include file that (in it's cascade) applies to said field instance. The same thing goes for the <em>Default Value</em> field. Another way to say this is that the file includes will override the field instance schema and default.</li>
</ol>
<h2>By Custom Module</h2>
<p>If using a custom module you will place these files in:</p>
<pre><code>my_module/includes/xml_field/
</code></pre>
<p>You must also implement <code>hook_xml_field_api()</code> in your module to let xml_field know to look in your module directory for these files. See <code>xml_field.api.php</code> for more info.</p>
<h2>By User Files</h2>
<p>You may also place include files in the public files of Drupal, however they will have the lowest weight when conflicting with a module-provided file of the same name.</p>
<pre><code>public://xml_field/
</code></pre>
<h2>You can use XML Field to embed YouTube videos.</h2>
<p>Here's a short tutorial with the minimum steps required to make this work.</p>
<h3>Configuration</h3>
<ol>
<li>Enable the <em>XML Field Extras</em> module.</li>
<li>Create a new XML field on your entity, calling it something like <em>YouTube Snippet</em>.</li>
<li>Set the validation type to: <strong>YouTube Snippets</strong></li>
<li>Leave the schema blank and uncheck all of the schema setting options.</li>
<li>Set up the default options as you desire.</li>
</ol>
<h3>Widget Type</h3>
<ol>
<li>Enable the XML Field CodeMirror module, if desired.</li>
<li>Set the widget type to CodeMirror XML.</li>
</ol>
<h3>Manage Display</h3>
<ol>
<li><strong>Set the format to RAW XML.</strong></li>
</ol>
<p>This recipe insures that only YouTube snippets are pasted into the field and that code is rendered directly to the browser.</p>
...@@ -3,7 +3,7 @@ project = xml_field ...@@ -3,7 +3,7 @@ project = xml_field
description = Defines an xml field type and toolkit. description = Defines an xml field type and toolkit.
package = Fields package = Fields
core = 7.x core = 7.x
version = "7.x-2.3-rc1" version = "7.x-2.3-rc2"
files[] = lib/loft_php_lib/src/AKlump/LoftLib/Xml/LoftXmlElement.php files[] = lib/loft_php_lib/src/AKlump/LoftLib/Xml/LoftXmlElement.php
files[] = includes/xmlFieldXMLElement.php files[] = includes/xmlFieldXMLElement.php
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment